From: plaa Date: Fri, 10 Feb 2012 18:00:43 +0000 (+0000) Subject: Support for configurable stage separation X-Git-Tag: upstream/12.03~1^2~63 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=601cb5b06651d08e9bc94153f0e946fb32e5d7cb;p=debian%2Fopenrocket Support for configurable stage separation git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@408 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/core/ChangeLog b/core/ChangeLog index 86aede34..871d5fbc 100644 --- a/core/ChangeLog +++ b/core/ChangeLog @@ -1,3 +1,11 @@ +2012-02-10 Sampo Niskanen + + * Configurable stage separation events + +2012-00-00 Doug Pedrick + + * RKT saving support + 2011-11-24 Sampo Niskanen * Released version 1.1.9 diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 58fd496a..43613cf3 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -674,6 +674,13 @@ ComponentCfgDlg.configuration = configuration ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Modify +!StageConfig +StageConfig.tab.Separation = Separation +StageConfig.tab.Separation.ttip = Stage separation options +StageConfig.separation.lbl.title = Select when this stage separates: +StageConfig.separation.lbl.plus = plus +StageConfig.separation.lbl.seconds = seconds + !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = Number of fins: EllipticalFinSetCfg.Rotation = Rotation: @@ -1120,6 +1127,14 @@ NoseCone.NoseCone = Nose cone Transition.Transition = Transition !Stage Stage.Stage = Stage + +Stage.SeparationEvent.UPPER_IGNITION = Upper stage motor ignition +Stage.SeparationEvent.IGNITION = Current stage motor ignition +Stage.SeparationEvent.BURNOUT = Current stage motor burnout +Stage.SeparationEvent.EJECTION = Current stage ejection charge +Stage.SeparationEvent.LAUNCH = Launch +Stage.SeparationEvent.NEVER = Never + ! BodyTube BodyTube.BodyTube = Body tube ! TubeCoupler diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java index ab8ef8de..77f7185b 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java @@ -478,6 +478,14 @@ class DocumentConfig { Reflection.findMethod(Rocket.class, "setDesigner", String.class))); setters.put("Rocket:revision", new StringSetter( Reflection.findMethod(Rocket.class, "setRevision", String.class))); + + // Stage + setters.put("Stage:separationevent", new EnumSetter( + Reflection.findMethod(Stage.class, "setSeparationEvent", Stage.SeparationEvent.class), + Stage.SeparationEvent.class)); + setters.put("Stage:separationdelay", new DoubleSetter( + Reflection.findMethod(Stage.class, "setSeparationDelay", double.class))); + } diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java index 0fd0f6f3..e0b1b58d 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/StageSaver.java @@ -1,20 +1,35 @@ package net.sf.openrocket.file.openrocket.savers; import java.util.ArrayList; +import java.util.List; -public class StageSaver extends ComponentAssemblySaver { +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; +public class StageSaver extends ComponentAssemblySaver { + private static final StageSaver instance = new StageSaver(); public static ArrayList getElements(net.sf.openrocket.rocketcomponent.RocketComponent c) { ArrayList list = new ArrayList(); list.add(""); - instance.addParams(c,list); + instance.addParams(c, list); list.add(""); return list; } - + @Override + protected void addParams(RocketComponent c, List elements) { + super.addParams(c, elements); + Stage stage = (Stage) c; + + if (stage.getStageNumber() > 0) { + elements.add("" + + stage.getSeparationEvent().name().toLowerCase().replace("_", "") + + ""); + elements.add("" + stage.getSeparationDelay() + ""); + } + } } diff --git a/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java b/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java new file mode 100644 index 00000000..21817297 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/configdialog/StageConfig.java @@ -0,0 +1,60 @@ +package net.sf.openrocket.gui.configdialog; + +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.adaptors.EnumModel; +import net.sf.openrocket.gui.components.StyledLabel; +import net.sf.openrocket.gui.components.StyledLabel.Style; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.Stage.SeparationEvent; +import net.sf.openrocket.startup.Application; + +public class StageConfig extends RocketComponentConfig { + private static final Translator trans = Application.getTranslator(); + + public StageConfig(OpenRocketDocument document, RocketComponent component) { + super(document, component); + + // Stage separation config (for non-first stage) + if (component.getStageNumber() > 0) { + JPanel tab = separationTab((Stage) component); + tabbedPane.insertTab(trans.get("tab.Separation"), null, tab, + trans.get("tab.Separation.ttip"), 1); + } + + } + + + private JPanel separationTab(Stage stage) { + JPanel panel = new JPanel(new MigLayout("fill")); + + // Select separation event + panel.add(new StyledLabel(trans.get("separation.lbl.title"), Style.BOLD), "spanx, wrap rel"); + + JComboBox combo = new JComboBox(new EnumModel(stage, "SeparationEvent")); + panel.add(combo, ""); + + // ... and delay + panel.add(new JLabel(trans.get("separation.lbl.plus")), ""); + + DoubleModel dm = new DoubleModel(stage, "SeparationDelay", 0); + JSpinner spin = new JSpinner(dm.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin)); + panel.add(spin, ""); + + //// seconds + panel.add(new JLabel(trans.get("separation.lbl.seconds")), "wrap unrel"); + + return panel; + } + +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/Stage.java b/core/src/net/sf/openrocket/rocketcomponent/Stage.java index f5079bfe..6ea9c297 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Stage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Stage.java @@ -1,34 +1,152 @@ package net.sf.openrocket.rocketcomponent; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.MathUtil; public class Stage extends ComponentAssembly { - + private static final Translator trans = Application.getTranslator(); - - @Override - public String getComponentName () { - //// Stage - return trans.get("Stage.Stage"); - } - - + + + public static enum SeparationEvent { + //// Upper stage motor ignition + UPPER_IGNITION("Stage.SeparationEvent.UPPER_IGNITION") { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.IGNITION) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition + 1); + } + }, + //// Current stage motor ignition + IGNITION("Stage.SeparationEvent.IGNITION") { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.IGNITION) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition); + } + }, + //// Current stage motor burnout + BURNOUT("Stage.SeparationEvent.BURNOUT") { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.BURNOUT) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition); + } + }, + //// Current stage ejection charge + EJECTION("Stage.SeparationEvent.EJECTION") { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + if (e.getType() != FlightEvent.Type.EJECTION_CHARGE) + return false; + + int ignition = e.getSource().getStageNumber(); + int mount = stage.getStageNumber(); + return (mount == ignition); + } + }, + //// Launch + LAUNCH("Stage.SeparationEvent.LAUNCH") { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + return e.getType() == FlightEvent.Type.LAUNCH; + } + }, + //// Never + NEVER("Stage.SeparationEvent.NEVER") { + @Override + public boolean isSeparationEvent(FlightEvent e, Stage stage) { + return false; + } + }, + ; + + + private final String description; + + SeparationEvent(String description) { + this.description = description; + } + + /** + * Test whether a specific event is a stage separation event. + */ + public abstract boolean isSeparationEvent(FlightEvent e, Stage stage); + + @Override + public String toString() { + return trans.get(description); + } + }; + + + private SeparationEvent separationEvent = SeparationEvent.UPPER_IGNITION; + private double separationDelay = 0; + + + @Override + public String getComponentName() { + //// Stage + return trans.get("Stage.Stage"); + } + + + public SeparationEvent getSeparationEvent() { + return separationEvent; + } + + + public void setSeparationEvent(SeparationEvent separationEvent) { + if (separationEvent == this.separationEvent) + return; + this.separationEvent = separationEvent; + fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); + } + + + public double getSeparationDelay() { + return separationDelay; + } + + + public void setSeparationDelay(double separationDelay) { + if (MathUtil.equals(separationDelay, this.separationDelay)) + return; + this.separationDelay = separationDelay; + fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE); + } + + + @Override public boolean allowsChildren() { return true; } - - /** + + /** * Check whether the given type can be added to this component. A Stage allows * only BodyComponents to be added. - * - * @param type The RocketComponent class type to add. - * - * @return Whether such a component can be added. - */ - @Override - public boolean isCompatible (Class type) { - return BodyComponent.class.isAssignableFrom(type); - } + * + * @param type The RocketComponent class type to add. + * + * @return Whether such a component can be added. + */ + @Override + public boolean isCompatible(Class type) { + return BodyComponent.class.isAssignableFrom(type); + } } diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 002fd62c..305516ad 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -16,6 +16,7 @@ import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.simulation.exception.MotorIgnitionException; import net.sf.openrocket.simulation.exception.SimulationException; import net.sf.openrocket.simulation.exception.SimulationLaunchException; @@ -60,7 +61,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { status = initialStatus(configuration, motorConfiguration, simulationConditions, flightData); status = currentStepper.initialize(status); - + SimulationListenerHelper.fireStartSimulation(status); // Get originating position (in case listener has modified launch position) Coordinate origin = status.getRocketPosition(); @@ -87,7 +88,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } SimulationListenerHelper.firePostStep(status); - + // Check for NaN values in the simulation status checkNaN(); @@ -100,7 +101,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { maxAlt = status.getRocketPosition().z; } - + // Position relative to start location Coordinate relativePosition = status.getRocketPosition().sub(origin); @@ -134,14 +135,14 @@ public class BasicEventSimulationEngine implements SimulationEngine { addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null)); } - + // Check for apogee if (!status.isApogeeReached() && status.getRocketPosition().z < maxAlt - 0.01) { addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(), status.getConfiguration().getRocket())); } - + // Check for burnt out motors for (MotorId motorId : status.getMotorConfiguration().getMotorIDs()) { MotorInstance motor = status.getMotorConfiguration().getMotorInstance(motorId); @@ -171,7 +172,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - + private SimulationStatus initialStatus(Configuration configuration, MotorInstanceConfiguration motorConfiguration, SimulationConditions simulationConditions, FlightData flightData) { @@ -201,7 +202,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { init.setRocketOrientationQuaternion(o); init.setRocketRotationVelocity(Coordinate.NUL); - + /* * Calculate the effective launch rod length taking into account launch lugs. * If no lugs are found, assume a tower launcher of full length. @@ -228,8 +229,8 @@ public class BasicEventSimulationEngine implements SimulationEngine { } init.setEffectiveLaunchRodLength(length); - - + + init.setSimulationStartWallTime(System.nanoTime()); init.setMotorIgnited(false); @@ -246,7 +247,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - + /** * Create a rocket configuration from the launch conditions. * @@ -262,7 +263,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - + /** * Create a new motor instance configuration for the rocket configuration. * @@ -302,6 +303,12 @@ public class BasicEventSimulationEngine implements SimulationEngine { for (event = nextEvent(); event != null; event = nextEvent()) { + // Ignore events for components that are no longer attached to the rocket + if (event.getSource() != null && event.getSource().getParent() != null && + !status.getConfiguration().isStageActive(event.getSource().getStageNumber())) { + continue; + } + // Call simulation listeners, allow aborting event handling if (!SimulationListenerHelper.fireHandleFlightEvent(status, event)) { continue; @@ -327,8 +334,8 @@ public class BasicEventSimulationEngine implements SimulationEngine { } } - - + + // Check for motor ignition events, add ignition events to queue for (MotorId id : status.getMotorConfiguration().getMotorIDs()) { MotorMount mount = status.getMotorConfiguration().getMotorMount(id); @@ -341,7 +348,20 @@ public class BasicEventSimulationEngine implements SimulationEngine { } } - + + // Check for stage separation event + for (int stageNo : status.getConfiguration().getActiveStages()) { + if (stageNo == 0) + continue; + + Stage stage = (Stage) status.getConfiguration().getRocket().getChild(stageNo); + if (stage.getSeparationEvent().isSeparationEvent(event, stage)) { + addEvent(new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, + event.getTime() + stage.getSeparationDelay(), stage)); + } + } + + // Check for recovery device deployment, add events to queue Iterator rci = status.getConfiguration().iterator(); while (rci.hasNext()) { @@ -355,7 +375,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } } - + // Handle event switch (event.getType()) { @@ -363,7 +383,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { status.getFlightData().addEvent(event); break; } - + case IGNITION: { // Ignite the motor MotorMount mount = (MotorMount) event.getSource(); @@ -374,31 +394,23 @@ public class BasicEventSimulationEngine implements SimulationEngine { status.setMotorIgnited(true); status.getFlightData().addEvent(event); - // Add stage separation event if appropriate - int n = component.getStageNumber(); - if (n < component.getRocket().getStageCount() - 1) { - if (status.getConfiguration().isStageActive(n + 1)) { - addEvent(new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, event.getTime(), - component.getStage())); - } - } break; } - + case LIFTOFF: { // Mark lift-off as occurred status.setLiftoff(true); status.getFlightData().addEvent(event); break; } - + case LAUNCHROD: { // Mark launch rod as cleared status.setLaunchRodCleared(true); status.getFlightData().addEvent(event); break; } - + case BURNOUT: { // If motor burnout occurs without lift-off, abort if (!status.isLiftoff()) { @@ -415,21 +427,21 @@ public class BasicEventSimulationEngine implements SimulationEngine { status.getFlightData().addEvent(event); break; } - + case EJECTION_CHARGE: { status.getFlightData().addEvent(event); break; } - + case STAGE_SEPARATION: { // TODO: HIGH: Store lower stages to be simulated later RocketComponent stage = event.getSource(); int n = stage.getStageNumber(); - status.getConfiguration().setToStage(n); + status.getConfiguration().setToStage(n - 1); status.getFlightData().addEvent(event); break; } - + case APOGEE: // Mark apogee as reached status.setApogeeReached(true); @@ -494,7 +506,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - + // If no motor has ignited, abort if (!status.isMotorIgnited()) { throw new MotorIgnitionException("No motors ignited."); @@ -503,7 +515,6 @@ public class BasicEventSimulationEngine implements SimulationEngine { return ret; } - /** * Add a flight event to the event queue unless a listener aborts adding it. * @@ -516,7 +527,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - + /** * Return the next flight event to handle, or null if no more events should be handled. * This method jumps the simulation time forward in case no motors have been ignited. @@ -543,7 +554,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - + private void checkNaN() throws SimulationException { double d = 0; boolean b = false; @@ -568,5 +579,5 @@ public class BasicEventSimulationEngine implements SimulationEngine { } } - + } diff --git a/core/src/net/sf/openrocket/simulation/FlightEvent.java b/core/src/net/sf/openrocket/simulation/FlightEvent.java index ead84eec..92f125c6 100644 --- a/core/src/net/sf/openrocket/simulation/FlightEvent.java +++ b/core/src/net/sf/openrocket/simulation/FlightEvent.java @@ -12,7 +12,7 @@ import net.sf.openrocket.startup.Application; */ public class FlightEvent implements Comparable { private static final Translator trans = Application.getTranslator(); - + /** * The type of the flight event. * @@ -22,68 +22,56 @@ public class FlightEvent implements Comparable { /** * Rocket launch. */ - //// Launch LAUNCH(trans.get("FlightEvent.Type.LAUNCH")), /** * Ignition of a motor. Source is the motor mount the motor of which has ignited, * and the data is the MotorId of the motor instance. */ - //// Motor ignition IGNITION(trans.get("FlightEvent.Type.IGNITION")), /** * When the motor has lifted off the ground. */ - //// Lift-off LIFTOFF(trans.get("FlightEvent.Type.LIFTOFF")), /** * Launch rod has been cleared. */ - //// Launch rod clearance LAUNCHROD(trans.get("FlightEvent.Type.LAUNCHROD")), /** * Burnout of a motor. Source is the motor mount the motor of which has burnt out, * and the data is the MotorId of the motor instance. */ - //// Motor burnout BURNOUT(trans.get("FlightEvent.Type.BURNOUT")), /** * Ejection charge of a motor fired. Source is the motor mount the motor of * which has exploded its ejection charge, and data is the MotorId of the motor instance. */ - //// Ejection charge EJECTION_CHARGE(trans.get("FlightEvent.Type.EJECTION_CHARGE")), /** - * Separation of a stage. Source is the stage which has separated all lower stages. + * Separation of a stage. Source is the stage which is being separated from the upper stages. */ - //// Stage separation STAGE_SEPARATION(trans.get("FlightEvent.Type.STAGE_SEPARATION")), /** * Apogee has been reached. */ - //// Apogee APOGEE(trans.get("FlightEvent.Type.APOGEE")), /** * Opening of a recovery device. Source is the RecoveryComponent which has opened. */ - //// Recovery device deployment RECOVERY_DEVICE_DEPLOYMENT(trans.get("FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT")), /** * Ground has been hit after flight. */ - //// Ground hit GROUND_HIT(trans.get("FlightEvent.Type.GROUND_HIT")), - + /** * End of simulation. Placing this to the queue will end the simulation. */ - //// Simulation end SIMULATION_END(trans.get("FlightEvent.Type.SIMULATION_END")), - + /** * A change in altitude has occurred. Data is a Pair * which contains the old and new altitudes. */ - //// Altitude change ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE")); private final String name; @@ -120,7 +108,7 @@ public class FlightEvent implements Comparable { } - + public Type getType() { return type; }