Merge branch 'kruland-integration' of github.com:plaa/openrocket into kruland-integration-ui
This commit is contained in:
commit
83fcf0894e
@ -1,4 +1,8 @@
|
||||
|
||||
2013-03-01 Kevin Ruland
|
||||
* Added simulation of tumble recovery based on experimentation done by Sampo Niskane. This is particularly useful
|
||||
for low power staged flights.
|
||||
|
||||
2012-10-01 Kevin Ruland
|
||||
* Extended "motor configuration" concept to cover more properties. The concept was renamed "Flight Configuration" and
|
||||
allows the user to override stage separation, recovery deployment, and motor ignition per flight configuration.
|
||||
@ -7,7 +11,7 @@
|
||||
usable. The user selects the configuration in a drop down with buttons for "add", "delete" and "rename". After selecting
|
||||
the configuration, the tabbed area below allows the user to change the configuration for this simulation.
|
||||
|
||||
2012-10-01 Kevin Rualnd
|
||||
2012-10-01 Kevin Ruland
|
||||
* Allow simulation of stages without a motor. Users in the past have attempted to simulate separate recovery of payload
|
||||
and booster by tricking OpenRocket using a dummy motor with trivial thrust curve. Now the user does not need to do this.
|
||||
There is an example of this provided in TARC Payloader.ork and Boosted Dart.ork
|
||||
@ -21,7 +25,6 @@
|
||||
* Modified the zoom and pan controls in the simulation plot. Added zoom in, out, and reset buttons. Added
|
||||
scroll with mouse wheel. If the alt key is used with either of these, only the domain is zoomed. Richard
|
||||
contributed a more logical mouse controlled zoom - right click and drag will zoom (either domain, range or both).
|
||||
|
||||
|
||||
2012-09-28 Sampo Niskane
|
||||
* Released version 12.09.1
|
||||
|
@ -43,3 +43,7 @@ The following file format versions exist:
|
||||
Added lowerstageseparation as recovery device deployment event.
|
||||
Added <datatypes> section for supporting datatypes other than
|
||||
internal ones. Currently only supports datatypes from custom expressions.
|
||||
|
||||
1.6: Introduced with OpenRocket 13.03. Added component Appearances (decals & paint)
|
||||
Added configurable parameters to recovery devices, motor ignition and separation.
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1383,6 +1383,7 @@ FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT = Recovery device deployment
|
||||
FlightEvent.Type.GROUND_HIT = Ground hit
|
||||
FlightEvent.Type.SIMULATION_END = Simulation end
|
||||
FlightEvent.Type.ALTITUDE = Altitude change
|
||||
FlightEvent.Type.TUMBLE = Tumbling
|
||||
|
||||
! ThrustCurveMotorColumns
|
||||
TCurveMotorCol.MANUFACTURER = Manufacturer
|
||||
|
@ -205,7 +205,7 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
|
||||
/*
|
||||
* File version 1.6 is required for:
|
||||
* - saving files using appearances and textures
|
||||
* - saving files using appearances and textures, flight configurations.
|
||||
*
|
||||
* File version 1.5 is requires for:
|
||||
* - saving designs using ComponentPrests
|
||||
@ -223,11 +223,12 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
* Otherwise use version 1.0.
|
||||
*/
|
||||
|
||||
// Search the rocket for any Appearnces (version 1.6)
|
||||
// Search the rocket for any Appearances or non-motor flight configurations (version 1.6)
|
||||
for (RocketComponent c : document.getRocket()) {
|
||||
if (c.getAppearance() != null) {
|
||||
return FILE_VERSION_DIVISOR + 6;
|
||||
}
|
||||
// FIXME - test for flight configurations
|
||||
}
|
||||
|
||||
// Search the rocket for any ComponentPresets (version 1.5)
|
||||
@ -447,7 +448,6 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip)
|
||||
throws IOException {
|
||||
double previousTime = -100000;
|
||||
|
@ -1,112 +0,0 @@
|
||||
package net.sf.openrocket.gui.dialogs;
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ListSelectionModel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.gui.main.ExampleDesignFile;
|
||||
import net.sf.openrocket.gui.util.GUIUtil;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
public class ExampleDesignDialog extends JDialog {
|
||||
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
private boolean open = false;
|
||||
private final JList designSelection;
|
||||
|
||||
private ExampleDesignDialog(ExampleDesignFile[] designs, Window parent) {
|
||||
//// Open example design
|
||||
super(parent, trans.get("exdesigndlg.lbl.Openexampledesign"), Dialog.ModalityType.APPLICATION_MODAL);
|
||||
|
||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||
|
||||
//// Select example designs to open:
|
||||
panel.add(new JLabel(trans.get("exdesigndlg.lbl.Selectexample")), "wrap");
|
||||
|
||||
designSelection = new JList(designs);
|
||||
designSelection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
designSelection.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() >= 2) {
|
||||
open = true;
|
||||
ExampleDesignDialog.this.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
panel.add(new JScrollPane(designSelection), "grow, wmin 300lp, wrap para");
|
||||
|
||||
//// Open button
|
||||
JButton openButton = new JButton(trans.get("exdesigndlg.but.open"));
|
||||
openButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
open = true;
|
||||
ExampleDesignDialog.this.setVisible(false);
|
||||
}
|
||||
});
|
||||
panel.add(openButton, "split 2, sizegroup buttons, growx");
|
||||
|
||||
//// Cancel button
|
||||
JButton cancelButton = new JButton(trans.get("dlg.but.cancel"));
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
open = false;
|
||||
ExampleDesignDialog.this.setVisible(false);
|
||||
}
|
||||
});
|
||||
panel.add(cancelButton, "sizegroup buttons, growx");
|
||||
|
||||
this.add(panel);
|
||||
this.pack();
|
||||
this.setLocationByPlatform(true);
|
||||
|
||||
GUIUtil.setDisposableDialogOptions(this, openButton);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open a dialog to allow opening the example designs.
|
||||
*
|
||||
* @param parent the parent window of the dialog.
|
||||
* @return an array of URL's to open, or <code>null</code> if the operation
|
||||
* was cancelled.
|
||||
*/
|
||||
public static URL[] selectExampleDesigns(Window parent) {
|
||||
|
||||
ExampleDesignFile[] designs = ExampleDesignFile.getExampleDesigns();
|
||||
|
||||
ExampleDesignDialog dialog = new ExampleDesignDialog(designs, parent);
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (!dialog.open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object[] selected = dialog.designSelection.getSelectedValues();
|
||||
URL[] urls = new URL[selected.length];
|
||||
int i = 0;
|
||||
for (Object obj : selected) {
|
||||
ExampleDesignFile file = (ExampleDesignFile) obj;
|
||||
urls[i++] = file.getURL();
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
}
|
@ -73,7 +73,6 @@ import net.sf.openrocket.gui.dialogs.BugReportDialog;
|
||||
import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
|
||||
import net.sf.openrocket.gui.dialogs.DebugLogDialog;
|
||||
import net.sf.openrocket.gui.dialogs.DetailDialog;
|
||||
import net.sf.openrocket.gui.dialogs.ExampleDesignDialog;
|
||||
import net.sf.openrocket.gui.dialogs.LicenseDialog;
|
||||
import net.sf.openrocket.gui.dialogs.PrintDialog;
|
||||
import net.sf.openrocket.gui.dialogs.ScaleDialog;
|
||||
@ -429,34 +428,12 @@ public class BasicFrame extends JFrame {
|
||||
item.setIcon(Icons.FILE_OPEN);
|
||||
menu.add(item);
|
||||
|
||||
// FIXME - one of these two example menu items needs to be removed.
|
||||
//// Open example...
|
||||
item = new ExampleDesignFileAction(trans.get("main.menu.file.openExample"), this);
|
||||
item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openexamplerocketdesign"));
|
||||
item.setIcon(Icons.FILE_OPEN_EXAMPLE);
|
||||
menu.add(item);
|
||||
|
||||
//// Open example...
|
||||
item = new JMenuItem(trans.get("main.menu.file.openExample"));
|
||||
item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openexamplerocketdesign"));
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
|
||||
SHORTCUT_KEY | ActionEvent.SHIFT_MASK));
|
||||
item.setIcon(Icons.FILE_OPEN_EXAMPLE);
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.user("Open example... selected");
|
||||
URL[] urls = ExampleDesignDialog.selectExampleDesigns(BasicFrame.this);
|
||||
if (urls != null) {
|
||||
for (URL u : urls) {
|
||||
log.user("Opening example " + u);
|
||||
open(u, BasicFrame.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
//// Save
|
||||
|
@ -36,6 +36,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
//// Total motion vs. time
|
||||
@ -49,6 +50,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
//// Flight side profile
|
||||
@ -60,6 +62,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
//// Stability vs. time
|
||||
@ -73,6 +76,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
//// Drag coefficients vs. Mach number
|
||||
@ -97,6 +101,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
//// Angle of attack and orientation vs. time
|
||||
@ -110,6 +115,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
//// Simulation time step and computation time
|
||||
@ -122,6 +128,7 @@ public class PlotConfiguration implements Cloneable {
|
||||
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
|
||||
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
|
||||
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
|
||||
config.setEvent(FlightEvent.Type.TUMBLE, true);
|
||||
configs.add(config);
|
||||
|
||||
DEFAULT_CONFIGURATIONS = configs.toArray(new PlotConfiguration[0]);
|
||||
|
@ -74,7 +74,7 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable
|
||||
}
|
||||
|
||||
public double getEjectionDelay(MotorId id) {
|
||||
return ignitionDelays.get(indexOf(id));
|
||||
return ejectionDelays.get(indexOf(id));
|
||||
}
|
||||
|
||||
public MotorMount getMotorMount(MotorId id) {
|
||||
@ -154,6 +154,7 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable
|
||||
clone.ids.addAll(this.ids);
|
||||
clone.mounts.addAll(this.mounts);
|
||||
clone.positions.addAll(this.positions);
|
||||
clone.ejectionDelays.addAll(this.ejectionDelays);
|
||||
clone.ignitionTimes.addAll(this.ignitionTimes);
|
||||
clone.ignitionEvents.addAll(this.ignitionEvents);
|
||||
clone.ignitionDelays.addAll(this.ignitionDelays);
|
||||
|
@ -38,6 +38,11 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
// TODO: MEDIUM: Allow selecting steppers
|
||||
private SimulationStepper flightStepper = new RK4SimulationStepper();
|
||||
private SimulationStepper landingStepper = new BasicLandingStepper();
|
||||
private SimulationStepper tumbleStepper = new BasicTumbleStepper();
|
||||
|
||||
// Constant holding 30 degress in radians. This is the AOA condition
|
||||
// necessary to transistion to tumbling.
|
||||
private final static double AOA_TUMBLE_CONDITION = Math.PI / 3.0;
|
||||
|
||||
private SimulationStepper currentStepper;
|
||||
|
||||
@ -193,6 +198,24 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Tumbling
|
||||
// Conditions for transision are:
|
||||
// apogee reached
|
||||
// and is not already tumbling
|
||||
// and not stable (cg > cp)
|
||||
// and aoa > 30
|
||||
|
||||
if (status.isApogeeReached() && !status.isTumbling()) {
|
||||
double cp = status.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION);
|
||||
double cg = status.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION);
|
||||
double aoa = status.getFlightData().getLast(FlightDataType.TYPE_AOA);
|
||||
if (cg > cp && aoa > AOA_TUMBLE_CONDITION) {
|
||||
addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, status.getSimulationTime()));
|
||||
status.setTumbling(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (SimulationException e) {
|
||||
@ -474,6 +497,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
case ALTITUDE:
|
||||
break;
|
||||
|
||||
case TUMBLE:
|
||||
this.currentStepper = this.tumbleStepper;
|
||||
this.status = currentStepper.initialize(status);
|
||||
status.getFlightData().addEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
74
core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java
Normal file
74
core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java
Normal file
@ -0,0 +1,74 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.sf.openrocket.motor.MotorInstanceConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.Configuration;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
|
||||
|
||||
public class BasicTumbleStatus extends SimulationStatus {
|
||||
|
||||
// Magic constants from techdoc.pdf
|
||||
private final static double cDFin = 1.42;
|
||||
private final static double cDBt = 0.56;
|
||||
// Fin efficiency. Index is number of fins. The 0th entry is arbitrary and used to
|
||||
// offset the indexes so finEff[1] is the coefficient for one fin from the table in techdoc.pdf
|
||||
private final static double[] finEff = { 0.0, 0.5, 1.0, 1.41, 1.81, 1.73, 1.90, 1.85 };
|
||||
|
||||
private double drag;
|
||||
|
||||
public BasicTumbleStatus(Configuration configuration,
|
||||
MotorInstanceConfiguration motorConfiguration,
|
||||
SimulationConditions simulationConditions) {
|
||||
super(configuration, motorConfiguration, simulationConditions);
|
||||
computeTumbleDrag();
|
||||
}
|
||||
|
||||
public BasicTumbleStatus(SimulationStatus orig) {
|
||||
super(orig);
|
||||
if (orig instanceof BasicTumbleStatus) {
|
||||
this.drag = ((BasicTumbleStatus) orig).drag;
|
||||
}
|
||||
}
|
||||
|
||||
public double getTumbleDrag() {
|
||||
return drag;
|
||||
}
|
||||
|
||||
|
||||
public void computeTumbleDrag() {
|
||||
|
||||
// Computed based on Sampo's experimentation as documented in the pdf.
|
||||
|
||||
// compute the fin and body tube projected areas
|
||||
double aFins = 0.0;
|
||||
double aBt = 0.0;
|
||||
Rocket r = this.getConfiguration().getRocket();
|
||||
Iterator<RocketComponent> componentIterator = r.iterator();
|
||||
while (componentIterator.hasNext()) {
|
||||
RocketComponent component = componentIterator.next();
|
||||
if (!component.isAerodynamic()) {
|
||||
continue;
|
||||
}
|
||||
if (component instanceof FinSet) {
|
||||
|
||||
double finComponent = ((FinSet) component).getFinArea();
|
||||
int finCount = ((FinSet) component).getFinCount();
|
||||
// check bounds on finCount.
|
||||
if (finCount >= finEff.length) {
|
||||
finCount = finEff.length - 1;
|
||||
}
|
||||
|
||||
aFins += finComponent * finEff[finCount];
|
||||
|
||||
} else if (component instanceof SymmetricComponent) {
|
||||
aBt += ((SymmetricComponent) component).getComponentPlanformArea();
|
||||
}
|
||||
}
|
||||
|
||||
drag = (cDFin * aFins + cDBt * aBt);
|
||||
}
|
||||
}
|
137
core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java
Normal file
137
core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java
Normal file
@ -0,0 +1,137 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
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 BasicTumbleStepper extends AbstractSimulationStepper {
|
||||
|
||||
private static final double RECOVERY_TIME_STEP = 0.5;
|
||||
|
||||
@Override
|
||||
public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
|
||||
return new BasicTumbleStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
|
||||
|
||||
// Get the atmospheric conditions
|
||||
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
|
||||
|
||||
//// Local wind speed and direction
|
||||
Coordinate windSpeed = modelWindVelocity(status);
|
||||
Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
|
||||
|
||||
// Get total CD
|
||||
double mach = airSpeed.length() / atmosphere.getMachSpeed();
|
||||
|
||||
double tumbleDrag = ((BasicTumbleStatus)status).getTumbleDrag();
|
||||
|
||||
// Compute drag force
|
||||
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
|
||||
double dragForce = tumbleDrag * dynP;
|
||||
MassData massData = calculateMassData(status);
|
||||
double mass = massData.getCG().weight;
|
||||
|
||||
|
||||
// Compute drag acceleration
|
||||
Coordinate linearAcceleration;
|
||||
if (airSpeed.length() > 0.001) {
|
||||
linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
|
||||
} else {
|
||||
linearAcceleration = Coordinate.NUL;
|
||||
}
|
||||
|
||||
// Add effect of gravity
|
||||
double gravity = modelGravity(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);
|
||||
|
||||
// Perform Euler integration
|
||||
status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
|
||||
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
|
||||
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
|
||||
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.addPoint();
|
||||
|
||||
data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
|
||||
data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
|
||||
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
|
||||
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
|
||||
if (extra) {
|
||||
data.setValue(FlightDataType.TYPE_POSITION_XY,
|
||||
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
|
||||
data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
|
||||
Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
|
||||
|
||||
data.setValue(FlightDataType.TYPE_VELOCITY_XY,
|
||||
MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
|
||||
data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
|
||||
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
|
||||
|
||||
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
|
||||
|
||||
double Re = airSpeed.length() *
|
||||
status.getConfiguration().getLength() /
|
||||
atmosphere.getKinematicViscosity();
|
||||
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
|
||||
}
|
||||
|
||||
|
||||
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
|
||||
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
|
||||
data.setValue(FlightDataType.TYPE_GRAVITY, gravity);
|
||||
|
||||
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);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
|
||||
data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_MASS, mass);
|
||||
data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, 0.0); // Is this a reasonable assumption? Probably.
|
||||
|
||||
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
|
||||
data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
|
||||
data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
|
||||
data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
|
||||
data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
|
||||
|
||||
data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
|
||||
data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
|
||||
(System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);
|
||||
}
|
||||
|
||||
}
|
@ -72,7 +72,12 @@ public class FlightEvent implements Comparable<FlightEvent> {
|
||||
* A change in altitude has occurred. Data is a <code>Pair<Double,Double></code>
|
||||
* which contains the old and new altitudes.
|
||||
*/
|
||||
ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE"));
|
||||
ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE")),
|
||||
|
||||
/**
|
||||
* The rocket begins to tumble.
|
||||
*/
|
||||
TUMBLE(trans.get("FlightEvent.Type.TUMBLE"));
|
||||
|
||||
private final String name;
|
||||
|
||||
|
@ -69,6 +69,9 @@ public class SimulationStatus implements Monitorable {
|
||||
/** Set to true when apogee has been detected. */
|
||||
private boolean apogeeReached = false;
|
||||
|
||||
/** Set to true to indicate the rocket is tumbling. */
|
||||
private boolean tumbling = false;
|
||||
|
||||
/** Contains a list of deployed recovery devices. */
|
||||
private MonitorableSet<RecoveryDevice> deployedRecoveryDevices = new MonitorableSet<RecoveryDevice>();
|
||||
|
||||
@ -179,6 +182,7 @@ public class SimulationStatus implements Monitorable {
|
||||
this.liftoff = orig.liftoff;
|
||||
this.launchRodCleared = orig.launchRodCleared;
|
||||
this.apogeeReached = orig.apogeeReached;
|
||||
this.tumbling = orig.tumbling;
|
||||
this.motorBurntOut = orig.motorBurntOut;
|
||||
|
||||
this.deployedRecoveryDevices.clear();
|
||||
@ -380,6 +384,15 @@ public class SimulationStatus implements Monitorable {
|
||||
}
|
||||
|
||||
|
||||
public void setTumbling( boolean tumbling ) {
|
||||
this.tumbling = tumbling;
|
||||
this.modID++;
|
||||
}
|
||||
|
||||
public boolean isTumbling() {
|
||||
return tumbling;
|
||||
}
|
||||
|
||||
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
|
||||
return deployedRecoveryDevices;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user