diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/AirStart.java b/core/src/net/sf/openrocket/simulation/extension/example/AirStart.java similarity index 91% rename from core/src/net/sf/openrocket/simulation/extension/impl/AirStart.java rename to core/src/net/sf/openrocket/simulation/extension/example/AirStart.java index e591685b3..835b757a4 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/AirStart.java +++ b/core/src/net/sf/openrocket/simulation/extension/example/AirStart.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.simulation.extension.impl; +package net.sf.openrocket.simulation.extension.example; import net.sf.openrocket.l10n.L10N; import net.sf.openrocket.simulation.SimulationConditions; @@ -28,6 +28,11 @@ public class AirStart extends AbstractSimulationExtension { name = L10N.replace(name, "{vel}", UnitGroup.UNITS_VELOCITY.toStringUnit(getLaunchVelocity())); return name; } + + @Override + public String getDescription() { + return "Start simulation with a configurable altitude and velocity"; + } public double getLaunchAltitude() { return config.getDouble("launchAltitude", 0.0); diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/AirStartProvider.java b/core/src/net/sf/openrocket/simulation/extension/example/AirStartProvider.java similarity index 84% rename from core/src/net/sf/openrocket/simulation/extension/impl/AirStartProvider.java rename to core/src/net/sf/openrocket/simulation/extension/example/AirStartProvider.java index f04458f35..133f5511d 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/AirStartProvider.java +++ b/core/src/net/sf/openrocket/simulation/extension/example/AirStartProvider.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.simulation.extension.impl; +package net.sf.openrocket.simulation.extension.example; import net.sf.openrocket.plugin.Plugin; import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider; diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/CSVSaveListener.java b/core/src/net/sf/openrocket/simulation/extension/example/CSVSave.java similarity index 70% rename from core/src/net/sf/openrocket/simulation/listeners/example/CSVSaveListener.java rename to core/src/net/sf/openrocket/simulation/extension/example/CSVSave.java index 05a55fa29..c8c925144 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/example/CSVSaveListener.java +++ b/core/src/net/sf/openrocket/simulation/extension/example/CSVSave.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.simulation.listeners.example; +package net.sf.openrocket.simulation.extension.example; import java.io.File; import java.io.FileNotFoundException; @@ -9,12 +9,14 @@ import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.SimulationConditions; import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtension; import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; -public class CSVSaveListener extends AbstractSimulationListener { +public class CSVSave extends AbstractSimulationExtension { private static enum Types { TIME { @@ -218,78 +220,89 @@ public class CSVSaveListener extends AbstractSimulationListener { private File file; private PrintStream output = null; - + + @Override + public String getDescription() { + return "dump a CSV file with a predetermined set of flight variables to a CSV file while running"; + } @Override - public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException { + public void initialize(SimulationConditions conditions) throws SimulationException { + conditions.getSimulationListenerList().add(new CSVSaveListener()); + } + + private class CSVSaveListener extends AbstractSimulationListener { - if (event.getType() == FlightEvent.Type.LAUNCH) { - int n = 1; + @Override + public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException { - if (output != null) { - System.err.println("WARNING: Ending simulation logging to CSV file " + - "(SIMULATION_END not encountered)."); + if (event.getType() == FlightEvent.Type.LAUNCH) { + int n = 1; + + if (output != null) { + System.err.println("WARNING: Ending simulation logging to CSV file " + + "(SIMULATION_END not encountered)."); + output.close(); + output = null; + } + + do { + file = new File(String.format(FILENAME_FORMAT, n)); + n++; + } while (file.exists()); + + System.err.println("Opening file " + file + " for CSV output."); + try { + output = new PrintStream(file); + } catch (FileNotFoundException e) { + System.err.println("ERROR OPENING FILE: " + e); + } + + final Types[] types = Types.values(); + StringBuilder s = new StringBuilder("# " + types[0].toString()); + for (int i = 1; i < types.length; i++) { + s.append("," + types[i].toString()); + } + output.println(s); + + } else if (event.getType() == FlightEvent.Type.SIMULATION_END && output != null) { + + System.err.println("Ending simulation logging to CSV file: " + file); output.close(); output = null; + + } else if (event.getType() != FlightEvent.Type.ALTITUDE) { + + if (output != null) { + output.println("# Event " + event); + } else { + System.err.println("WARNING: Event " + event + " encountered without open file"); + } + } - do { - file = new File(String.format(FILENAME_FORMAT, n)); - n++; - } while (file.exists()); - - System.err.println("Opening file " + file + " for CSV output."); - try { - output = new PrintStream(file); - } catch (FileNotFoundException e) { - System.err.println("ERROR OPENING FILE: " + e); - } + return true; + } + + @Override + public void postStep(SimulationStatus status) throws SimulationException { final Types[] types = Types.values(); - StringBuilder s = new StringBuilder("# " + types[0].toString()); - for (int i = 1; i < types.length; i++) { - s.append("," + types[i].toString()); - } - output.println(s); - - } else if (event.getType() == FlightEvent.Type.SIMULATION_END && output != null) { - - System.err.println("Ending simulation logging to CSV file: " + file); - output.close(); - output = null; - - } else if (event.getType() != FlightEvent.Type.ALTITUDE) { + StringBuilder s; if (output != null) { - output.println("# Event " + event); + + s = new StringBuilder("" + types[0].getValue(status)); + for (int i = 1; i < types.length; i++) { + s.append("," + types[i].getValue(status)); + } + output.println(s); + } else { - System.err.println("WARNING: Event " + event + " encountered without open file"); + + System.err.println("WARNING: stepTaken called with no open file " + + "(t=" + status.getSimulationTime() + ")"); } - } - - return true; - } - - @Override - public void postStep(SimulationStatus status) throws SimulationException { - - final Types[] types = Types.values(); - StringBuilder s; - - if (output != null) { - - s = new StringBuilder("" + types[0].getValue(status)); - for (int i = 1; i < types.length; i++) { - s.append("," + types[i].getValue(status)); - } - output.println(s); - - } else { - - System.err.println("WARNING: stepTaken called with no open file " + - "(t=" + status.getSimulationTime() + ")"); - } - } } diff --git a/core/src/net/sf/openrocket/simulation/extension/example/CSVSaveProvider.java b/core/src/net/sf/openrocket/simulation/extension/example/CSVSaveProvider.java new file mode 100644 index 000000000..92b9e0a83 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/CSVSaveProvider.java @@ -0,0 +1,13 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider; + +@Plugin +public class CSVSaveProvider extends AbstractSimulationExtensionProvider { + + public CSVSaveProvider() { + super(CSVSave.class, "Reports", "CSV Save"); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/DampingMoment.java b/core/src/net/sf/openrocket/simulation/extension/example/DampingMoment.java new file mode 100644 index 000000000..a6e0429fb --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/DampingMoment.java @@ -0,0 +1,147 @@ +package net.sf.openrocket.simulation.extension.example; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sf.openrocket.aerodynamics.AerodynamicCalculator; +import net.sf.openrocket.aerodynamics.AerodynamicForces; +import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.motor.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.SimulationConditions; +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtension; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; +import net.sf.openrocket.unit.UnitGroup; + +public class DampingMoment extends AbstractSimulationExtension { + private static final Logger log = LoggerFactory.getLogger(DampingMoment.class); + + // Save it as a FlightDataType + private static final FlightDataType cdm = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT); + private static final ArrayList types = new ArrayList(); + + DampingMoment() { + types.add(cdm); + } + + @Override + public List getFlightDataTypes() { + return types; + } + + @Override + public void initialize(SimulationConditions conditions) throws SimulationException { + log.debug("initializing..."); + conditions.getSimulationListenerList().add(new DampingMomentListener()); + } + + @Override + public String getName() { + return "Damping Moment Coeficient(Cdm)"; + } + + @Override + public String getDescription() { + return "Calculate damping moment coefficient after every simulation step and publish to FlightData as Cdm"; + } + + private class DampingMomentListener extends AbstractSimulationListener { + + @Override + public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException { + + //status.getFlightData().setValue(cdm, aerodynamicPart + propulsivePart); + status.getFlightData().setValue(cdm, calculate(status, flightConditions)); + + return flightConditions; + } + + private double calculate(SimulationStatus status, FlightConditions flightConditions) { + + // Work out the propulsive/jet damping part of the moment. + + // dm/dt = (thrust - ma)/v + FlightDataBranch data = status.getFlightData(); + + List mpAll = data.get(FlightDataType.TYPE_MOTOR_MASS); + List time = data.get(FlightDataType.TYPE_TIME); + if (mpAll == null || time == null) { + return Double.NaN; + } + + int len = mpAll.size(); + + // This isn't as accurate as I would like + double mdot = Double.NaN; + if (len > 2) { + // Using polynomial interpolator for derivative. Doesn't help much + //double[] x = { time.get(len-5), time.get(len-4), time.get(len-3), time.get(len-2), time.get(len-1) }; + //double[] y = { mpAll.get(len-5), mpAll.get(len-4), mpAll.get(len-3), mpAll.get(len-2), mpAll.get(len-1) }; + //PolyInterpolator interp = new PolyInterpolator(x); + //double[] coeff = interp.interpolator(y); + //double dt = .01; + //mdot = (interp.eval(x[4], coeff) - interp.eval(x[4]-dt, coeff))/dt; + + mdot = (mpAll.get(len - 1) - mpAll.get(len - 2)) / (time.get(len - 1) - time.get(len - 2)); + } + + double cg = data.getLast(FlightDataType.TYPE_CG_LOCATION); + + // find the maximum distance from nose to nozzle. + double nozzleDistance = 0; + FlightConfiguration config = status.getConfiguration(); + for (MotorConfiguration inst : config.getActiveMotors()) { + double x_position= inst.getX(); + double x = x_position + inst.getMotor().getLaunchCGx(); + if (x > nozzleDistance) { + nozzleDistance = x; + } + } + + // now can get the propulsive part + double propulsivePart = mdot * Math.pow(nozzleDistance - cg, 2); + + // Work out the aerodynamic part of the moment. + double aerodynamicPart = 0; + + AerodynamicCalculator aerocalc = status.getSimulationConditions().getAerodynamicCalculator(); + + // Must go through each component ... + Map forces = aerocalc.getForceAnalysis(status.getConfiguration(), flightConditions, status.getWarnings()); + for (Map.Entry entry : forces.entrySet()) { + + RocketComponent comp = entry.getKey(); + + if (!comp.isAerodynamic()) + continue; + + //System.out.println(comp.toString()); + + double CNa = entry.getValue().getCNa(); //? + double Cp = entry.getValue().getCP().length(); + double z = comp.getAxialOffset(); + + aerodynamicPart += CNa * Math.pow(z - Cp, 2); + } + + double v = flightConditions.getVelocity(); + double rho = flightConditions.getAtmosphericConditions().getDensity(); + double ar = flightConditions.getRefArea(); + + aerodynamicPart = aerodynamicPart * .5 * rho * v * ar; + + return aerodynamicPart + propulsivePart; + } + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/DampingMomentProvider.java b/core/src/net/sf/openrocket/simulation/extension/example/DampingMomentProvider.java new file mode 100644 index 000000000..902329613 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/DampingMomentProvider.java @@ -0,0 +1,13 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider; + +@Plugin +public class DampingMomentProvider extends AbstractSimulationExtensionProvider { + + public DampingMomentProvider() { + super(DampingMoment.class, "Post-step flight conditions", "Damping Moment Coefficient (Cdm)"); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/PrintSimulation.java b/core/src/net/sf/openrocket/simulation/extension/example/PrintSimulation.java new file mode 100644 index 000000000..99cf276ff --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/PrintSimulation.java @@ -0,0 +1,53 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.simulation.FlightDataBranch; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.SimulationConditions; +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtension; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; + +public class PrintSimulation extends AbstractSimulationExtension { + + @Override + public void initialize(SimulationConditions conditions) throws SimulationException { + conditions.getSimulationListenerList().add(new PrintSimulationListener()); + } + + @Override + public String getName() { + return "Print Simulation Values"; + } + + @Override + public String getDescription() { + return "Print summary of simulation progress to standard output after every step"; + } + + private class PrintSimulationListener extends AbstractSimulationListener { + + @Override + public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException { + System.out.println("*** handleEvent *** " + event.toString() + + " position=" + status.getRocketPosition() + " velocity=" + status.getRocketVelocity()); + return true; + } + + @Override + public void postStep(SimulationStatus status) throws SimulationException { + FlightDataBranch data = status.getFlightData(); + System.out.printf("*** stepTaken *** time=%.3f position=" + status.getRocketPosition() + + " velocity=" + status.getRocketVelocity() + "=%.3f\n", status.getSimulationTime(), status.getRocketVelocity().length()); + System.out.printf(" thrust=%.3fN drag==%.3fN mass=%.3fkg " + + "accZ=%.3fm/s2 acc=%.3fm/s2\n", + data.getLast(FlightDataType.TYPE_THRUST_FORCE), + data.getLast(FlightDataType.TYPE_DRAG_FORCE), + data.getLast(FlightDataType.TYPE_MASS), + data.getLast(FlightDataType.TYPE_ACCELERATION_Z), + data.getLast(FlightDataType.TYPE_ACCELERATION_TOTAL)); + } + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/PrintSimulationProvider.java b/core/src/net/sf/openrocket/simulation/extension/example/PrintSimulationProvider.java new file mode 100644 index 000000000..ebb4c3b8b --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/PrintSimulationProvider.java @@ -0,0 +1,13 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider; + +@Plugin +public class PrintSimulationProvider extends AbstractSimulationExtensionProvider { + + public PrintSimulationProvider() { + super(PrintSimulation.class, "Reports", "Print Simulation"); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/RollControl.java b/core/src/net/sf/openrocket/simulation/extension/example/RollControl.java new file mode 100644 index 000000000..8b3a0a42a --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/RollControl.java @@ -0,0 +1,206 @@ +package net.sf.openrocket.simulation.extension.example; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.SimulationConditions; +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtension; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.MathUtil; + +/** + * An example listener that applies a PI-controller to adjust the cant of fins + * to control the rocket's roll rate + * + * @author Sampo Niskanen + * + * One note: making aerodynamic changes during the simulation results in the simulation + * running *extremely* slowly + */ +public class RollControl extends AbstractSimulationExtension { + + // save fin cant angle as a FlightDataType + private static final ArrayList types = new ArrayList(); + private static final FlightDataType FIN_CANT_TYPE = FlightDataType.getType("Control fin cant", "\u03B1fc", UnitGroup.UNITS_ANGLE); + + @Override + public void initialize(SimulationConditions conditions) throws SimulationException { + conditions.getSimulationListenerList().add(new RollControlListener()); + } + + @Override + public String getName() { + return "Roll Control"; + } + + @Override + public String getDescription() { + return "Use a PID control to control a rocket's roll. The current cant angle of the control finset is published to flight data as \u03B1fc. " + + "Since this extension modifies design parameters during the simulation, it causes the simulation to run much more slowly."; + } + + @Override + public List getFlightDataTypes() { + return types; + } + + RollControl() { + types.add(FIN_CANT_TYPE); + } + + public String getControlFinName() { + return config.getString("controlFinName", "CONTROL"); + } + + public void setControlFinName(String name) { + config.put("controlFinName", name); + fireChangeEvent(); + } + + public double getStartTime() { + return config.getDouble("startTime", 0.5); + } + + public void setStartTime(double startTime) { + config.put("startTime", startTime); + fireChangeEvent(); + } + + // Desired roll rate (rad/sec) + public double getSetPoint() { + return config.getDouble("setPoint", 0.0); + } + + public void setSetPoint(double rollRate) { + config.put("setPoint", rollRate); + fireChangeEvent(); + } + + // Maximum control fin turn rate (rad/sec) + public double getFinRate() { + return config.getDouble("finRate", 10 * Math.PI/180); + } + + public void setFinRate(double finRate) { + config.put("finRate", finRate); + fireChangeEvent(); + } + + // Maximum control fin angle (rad) + public double getMaxFinAngle() { + return config.getDouble("maxFinAngle", 15 * Math.PI / 180); + } + + public void setMaxFinAngle(double maxFin) { + config.put("maxFinAngle", maxFin); + fireChangeEvent(); + } + + public double getKP() { + return config.getDouble("KP", 0.007); + } + + public void setKP(double KP) { + config.put("KP", KP); + fireChangeEvent(); + } + + public double getKI() { + return config.getDouble("KI", 0.2); + } + + public void setKI(double KI) { + config.put("KI", KI); + fireChangeEvent(); + } + + private class RollControlListener extends AbstractSimulationListener { + private FinSet finset; + + private double rollRate; + + private double prevTime = 0; + private double intState = 0; + + private double initialFinPosition; + private double finPosition = 0; + + @Override + public void startSimulation(SimulationStatus status) throws SimulationException { + + // Find the fin set + finset = null; + for (RocketComponent c : status.getConfiguration().getActiveComponents()) { + if ((c instanceof FinSet) && c.getName().equals(getControlFinName())) { + finset = (FinSet) c; + break; + } + } + if (finset == null) { + throw new SimulationException("A fin set with name '" + getControlFinName() + "' was not found"); + } + + // remember the initial fin position so we can set it back after running the simulation + initialFinPosition = finset.getCantAngle(); + } + + @Override + public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) { + // Store the current roll rate for later use + rollRate = flightConditions.getRollRate(); + return null; + } + + @Override + public void postStep(SimulationStatus status) throws SimulationException { + // Activate PID controller only after a specific time + if (status.getSimulationTime() < getStartTime()) { + prevTime = status.getSimulationTime(); + return; + } + + // Determine time step + double deltaT = status.getSimulationTime() - prevTime; + prevTime = status.getSimulationTime(); + + // PID controller + double error = getSetPoint() - rollRate; + + double p = getKP() * error; + intState += error * deltaT; + double i = getKI() * intState; + + double value = p + i; + + // Limit the fin turn rate + if (finPosition < value) { + finPosition = Math.min(finPosition + getFinRate() * deltaT, value); + } else { + finPosition = Math.max(finPosition - getFinRate() * deltaT, value); + } + + // Clamp the fin angle between bounds + if (Math.abs(value) > getMaxFinAngle()) { + System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n", + value * 180 / Math.PI, status.getSimulationTime()); + value = MathUtil.clamp(value, -getMaxFinAngle(), getMaxFinAngle()); + } + + // Set the control fin cant and store the data + finset.setCantAngle(finPosition); + status.getFlightData().setValue(FIN_CANT_TYPE, finPosition); + } + + @Override + public void endSimulation(SimulationStatus status, SimulationException exception) { + finset.setCantAngle(initialFinPosition); + } + } +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/RollControlProvider.java b/core/src/net/sf/openrocket/simulation/extension/example/RollControlProvider.java new file mode 100644 index 000000000..07bff21b5 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/RollControlProvider.java @@ -0,0 +1,13 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider; + +@Plugin +public class RollControlProvider extends AbstractSimulationExtensionProvider { + + public RollControlProvider() { + super(RollControl.class, "Control Enhancements", "Roll Control"); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/StopSimulation.java b/core/src/net/sf/openrocket/simulation/extension/example/StopSimulation.java new file mode 100644 index 000000000..ed01ca556 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/StopSimulation.java @@ -0,0 +1,95 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.SimulationConditions; +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtension; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; + +/** + * A simulation listener that stops the simulation after a specified number of steps or + * after a specified amount of simulation time. + * + * @author Sampo Niskanen + */ +public class StopSimulation extends AbstractSimulationExtension { + + @Override + public void initialize(SimulationConditions conditions) throws SimulationException { + conditions.getSimulationListenerList().add(new StopSimulationListener()); + } + + @Override + public String getName() { + return "Stop Simulation"; + } + + @Override + public String getDescription() { + return "Stop simulafter at a configurable simulation time or step count"; + } + + public int getReportRate() { + return config.getInt("reportRate", 500); + } + + public void setReportRate(int reportRate) { + config.put("reportRate", reportRate); + } + + public int getStopStep() { + return config.getInt("stopStep", 5000); + } + + public void setStopStep(int stopStep) { + config.put("stopStep", stopStep); + } + + public int getStopTime() { + return config.getInt("stopTime", 10); + } + + public void setStopTime(int stopTime) { + config.put("stopTime", stopTime); + } + + private class StopSimulationListener extends AbstractSimulationListener { + + private int step = 0; + + private long startTime = -1; + private long time = -1; + + @Override + public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) { + + if (event.getType() == FlightEvent.Type.LAUNCH) { + System.out.println("Simulation starting."); + time = System.nanoTime(); + startTime = System.nanoTime(); + } + + return true; + } + + + @Override + public void postStep(SimulationStatus status) throws SimulationException { + step++; + if ((step % getReportRate()) == 0) { + long t = System.nanoTime(); + + System.out.printf("Step %4d, time=%.3f, took %d us/step (avg. %d us/step)\n", + step, status.getSimulationTime(), (t - time) / 1000 / getReportRate(), (t - startTime) / 1000 / step); + time = t; + } + if (status.getSimulationTime() >= getStopTime() || step >= getStopStep()) { + System.out.printf("Stopping simulation, step=%d time=%.3f\n", step, status.getSimulationTime()); + status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, + status.getSimulationTime(), null)); + } + } + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/example/StopSimulationProvider.java b/core/src/net/sf/openrocket/simulation/extension/example/StopSimulationProvider.java new file mode 100644 index 000000000..fc6ec3928 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/extension/example/StopSimulationProvider.java @@ -0,0 +1,13 @@ +package net.sf.openrocket.simulation.extension.example; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider; + +@Plugin +public class StopSimulationProvider extends AbstractSimulationExtensionProvider { + + public StopSimulationProvider() { + super(StopSimulation.class, "Simulation Conditions", "Stop Simulation"); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/JavaCodeProvider.java b/core/src/net/sf/openrocket/simulation/extension/impl/JavaCodeProvider.java index 188fe1cc1..c53c74567 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/JavaCodeProvider.java +++ b/core/src/net/sf/openrocket/simulation/extension/impl/JavaCodeProvider.java @@ -7,7 +7,7 @@ import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvide public class JavaCodeProvider extends AbstractSimulationExtensionProvider { public JavaCodeProvider() { - super(JavaCode.class, "User code", "Java code"); + super(JavaCode.class, "Scripts", "Java listeners"); } } diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingProvider.java b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingProvider.java index 911b47214..41fe99644 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingProvider.java +++ b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingProvider.java @@ -7,7 +7,7 @@ import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvide public class ScriptingProvider extends AbstractSimulationExtensionProvider { public ScriptingProvider() { - super(ScriptingExtension.class, "User code", "Scripts"); + super(ScriptingExtension.class, "Scripts", "JavaScript"); } } diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java b/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java deleted file mode 100644 index 6d5839e07..000000000 --- a/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java +++ /dev/null @@ -1,116 +0,0 @@ -package net.sf.openrocket.simulation.listeners.example; - -import java.util.List; -import java.util.Map; - -import net.sf.openrocket.aerodynamics.AerodynamicCalculator; -import net.sf.openrocket.aerodynamics.AerodynamicForces; -import net.sf.openrocket.aerodynamics.FlightConditions; -import net.sf.openrocket.aerodynamics.WarningSet; -import net.sf.openrocket.motor.MotorConfiguration; -import net.sf.openrocket.rocketcomponent.FlightConfiguration; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.simulation.FlightDataBranch; -import net.sf.openrocket.simulation.FlightDataType; -import net.sf.openrocket.simulation.SimulationStatus; -import net.sf.openrocket.simulation.exception.SimulationException; -import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; -import net.sf.openrocket.unit.UnitGroup; - -public class DampingMoment extends AbstractSimulationListener { - - private static final FlightDataType type = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT); - - // unused - //private static final FlightDataType[] typeList = { type }; - - @Override - public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException { - - // Save it as a flightdatatype - - //status.getFlightData().setValue(type, aerodynamicPart + propulsivePart); - status.getFlightData().setValue(type, calculate(status, flightConditions)); - - return flightConditions; - } - - private double calculate(SimulationStatus status, FlightConditions flightConditions) { - - // Work out the propulsive/jet damping part of the moment. - - // dm/dt = (thrust - ma)/v - FlightDataBranch data = status.getFlightData(); - - List mpAll = data.get(FlightDataType.TYPE_MOTOR_MASS); - List time = data.get(FlightDataType.TYPE_TIME); - if (mpAll == null || time == null) { - return Double.NaN; - } - - int len = mpAll.size(); - - // This isn't as accurate as I would like - double mdot = Double.NaN; - if (len > 2) { - // Using polynomial interpolator for derivative. Doesn't help much - //double[] x = { time.get(len-5), time.get(len-4), time.get(len-3), time.get(len-2), time.get(len-1) }; - //double[] y = { mpAll.get(len-5), mpAll.get(len-4), mpAll.get(len-3), mpAll.get(len-2), mpAll.get(len-1) }; - //PolyInterpolator interp = new PolyInterpolator(x); - //double[] coeff = interp.interpolator(y); - //double dt = .01; - //mdot = (interp.eval(x[4], coeff) - interp.eval(x[4]-dt, coeff))/dt; - - mdot = (mpAll.get(len - 1) - mpAll.get(len - 2)) / (time.get(len - 1) - time.get(len - 2)); - } - - double cg = data.getLast(FlightDataType.TYPE_CG_LOCATION); - - // find the maximum distance from nose to nozzle. - double nozzleDistance = 0; - FlightConfiguration config = status.getConfiguration(); - for (MotorConfiguration inst : config.getActiveMotors()) { - double x_position= inst.getX(); - double x = x_position + inst.getMotor().getLaunchCGx(); - if (x > nozzleDistance) { - nozzleDistance = x; - } - } - - // now can get the propulsive part - double propulsivePart = mdot * Math.pow(nozzleDistance - cg, 2); - - // Work out the aerodynamic part of the moment. - double aerodynamicPart = 0; - - AerodynamicCalculator aerocalc = status.getSimulationConditions().getAerodynamicCalculator(); - - // Must go through each component ... - Map forces = aerocalc.getForceAnalysis(status.getConfiguration(), flightConditions, status.getWarnings()); - for (Map.Entry entry : forces.entrySet()) { - - RocketComponent comp = entry.getKey(); - - if (!comp.isAerodynamic()) - continue; - - //System.out.println(comp.toString()); - - double CNa = entry.getValue().getCNa(); //? - double Cp = entry.getValue().getCP().length(); - double z = comp.getAxialOffset(); - - aerodynamicPart += CNa * Math.pow(z - Cp, 2); - } - - double v = flightConditions.getVelocity(); - double rho = flightConditions.getAtmosphericConditions().getDensity(); - double ar = flightConditions.getRefArea(); - - aerodynamicPart = aerodynamicPart * .5 * rho * v * ar; - - return aerodynamicPart + propulsivePart; - - } - -} diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/PrintSimulationListener.java b/core/src/net/sf/openrocket/simulation/listeners/example/PrintSimulationListener.java deleted file mode 100644 index ac070d2bc..000000000 --- a/core/src/net/sf/openrocket/simulation/listeners/example/PrintSimulationListener.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.sf.openrocket.simulation.listeners.example; - -import net.sf.openrocket.simulation.FlightDataBranch; -import net.sf.openrocket.simulation.FlightDataType; -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.simulation.SimulationStatus; -import net.sf.openrocket.simulation.exception.SimulationException; -import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; - - -public class PrintSimulationListener extends AbstractSimulationListener { - - @Override - public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException { - System.out.println("*** handleEvent *** " + event.toString() + - " position=" + status.getRocketPosition() + " velocity=" + status.getRocketVelocity()); - return true; - } - - @Override - public void postStep(SimulationStatus status) throws SimulationException { - FlightDataBranch data = status.getFlightData(); - System.out.printf("*** stepTaken *** time=%.3f position=" + status.getRocketPosition() + - " velocity=" + status.getRocketVelocity() + "=%.3f\n", status.getSimulationTime(), status.getRocketVelocity().length()); - System.out.printf(" thrust=%.3fN drag==%.3fN mass=%.3fkg " + - "accZ=%.3fm/s2 acc=%.3fm/s2\n", - data.getLast(FlightDataType.TYPE_THRUST_FORCE), - data.getLast(FlightDataType.TYPE_DRAG_FORCE), - data.getLast(FlightDataType.TYPE_MASS), - data.getLast(FlightDataType.TYPE_ACCELERATION_Z), - data.getLast(FlightDataType.TYPE_ACCELERATION_TOTAL)); - } - -} diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java deleted file mode 100644 index 5493c4a0d..000000000 --- a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java +++ /dev/null @@ -1,114 +0,0 @@ -package net.sf.openrocket.simulation.listeners.example; - -import net.sf.openrocket.aerodynamics.FlightConditions; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.simulation.FlightDataType; -import net.sf.openrocket.simulation.SimulationStatus; -import net.sf.openrocket.simulation.exception.SimulationException; -import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; -import net.sf.openrocket.unit.UnitGroup; -import net.sf.openrocket.util.MathUtil; - -/** - * An example listener that applies a PI-controller to adjust the cant of fins - * named "CONTROL" to stop the rocket from rolling. - * - * @author Sampo Niskanen - */ -public class RollControlListener extends AbstractSimulationListener { - - // Name of control fin set - private static final String CONTROL_FIN_NAME = "CONTROL"; - - // Define custom flight data type - private static final FlightDataType FIN_CANT_TYPE = FlightDataType.getType("Control fin cant", "\u03B1fc", UnitGroup.UNITS_ANGLE); - - // Simulation time at which PID controller is activated - private static final double START_TIME = 0.5; - - // Desired roll rate (rad/sec) - private static final double SETPOINT = 0.0; - - // Maximum control fin turn rate (rad/sec) - private static final double TURNRATE = 10 * Math.PI / 180; - - // Maximum control fin angle (rad) - private static final double MAX_ANGLE = 15 * Math.PI / 180; - - /* - * PID parameters - * - * At M=0.3 KP oscillation threshold between 0.35 and 0.4. Good KI=3 - * At M=0.6 KP oscillation threshold between 0.07 and 0.08 Good KI=2 - * At M=0.9 KP oscillation threshold between 0.013 and 0.014 Good KI=0.5 - */ - private static final double KP = 0.007; - private static final double KI = 0.2; - - private double rollRate; - - private double prevTime = 0; - private double intState = 0; - - private double finPosition = 0; - - @Override - public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) { - // Store the current roll rate for later use - rollRate = flightConditions.getRollRate(); - return null; - } - - @Override - public void postStep(SimulationStatus status) throws SimulationException { - // Activate PID controller only after a specific time - if (status.getSimulationTime() < START_TIME) { - prevTime = status.getSimulationTime(); - return; - } - - // Find the fin set named CONTROL - FinSet finset = null; - for (RocketComponent c : status.getConfiguration().getActiveComponents()) { - if ((c instanceof FinSet) && (c.getName().equals(CONTROL_FIN_NAME))) { - finset = (FinSet) c; - break; - } - } - if (finset == null) { - throw new SimulationException("A fin set with name '" + CONTROL_FIN_NAME + "' was not found"); - } - - // Determine time step - double deltaT = status.getSimulationTime() - prevTime; - prevTime = status.getSimulationTime(); - - // PID controller - double error = SETPOINT - rollRate; - - double p = KP * error; - intState += error * deltaT; - double i = KI * intState; - - double value = p + i; - - // Clamp the fin angle between -MAX_ANGLE and MAX_ANGLE - if (Math.abs(value) > MAX_ANGLE) { - System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n", - value * 180 / Math.PI, status.getSimulationTime()); - value = MathUtil.clamp(value, -MAX_ANGLE, MAX_ANGLE); - } - - // Limit the fin turn rate - if (finPosition < value) { - finPosition = Math.min(finPosition + TURNRATE * deltaT, value); - } else { - finPosition = Math.max(finPosition - TURNRATE * deltaT, value); - } - - // Set the control fin cant and store the data - finset.setCantAngle(finPosition); - status.getFlightData().setValue(FIN_CANT_TYPE, finPosition); - } -} diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/StopSimulationListener.java b/core/src/net/sf/openrocket/simulation/listeners/example/StopSimulationListener.java deleted file mode 100644 index a76461696..000000000 --- a/core/src/net/sf/openrocket/simulation/listeners/example/StopSimulationListener.java +++ /dev/null @@ -1,65 +0,0 @@ -package net.sf.openrocket.simulation.listeners.example; - -import net.sf.openrocket.simulation.FlightEvent; -import net.sf.openrocket.simulation.SimulationStatus; -import net.sf.openrocket.simulation.exception.SimulationException; -import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; - -/** - * A simulation listener that stops the simulation after a specified number of steps or - * after a specified amount of simulation time. - * - * @author Sampo Niskanen - */ -public class StopSimulationListener extends AbstractSimulationListener { - - private final int REPORT = 500; - - private final double stopTime; - private final int stopStep; - - private int step = 0; - - private long startTime = -1; - private long time = -1; - - public StopSimulationListener() { - this(0, 0); - } - - public StopSimulationListener(double t, int n) { - stopTime = t; - stopStep = n; - } - - @Override - public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) { - - if (event.getType() == FlightEvent.Type.LAUNCH) { - System.out.println("Simulation starting."); - time = System.nanoTime(); - startTime = System.nanoTime(); - } - - return true; - } - - - @Override - public void postStep(SimulationStatus status) throws SimulationException { - step++; - if ((step % REPORT) == 0) { - long t = System.nanoTime(); - - System.out.printf("Step %4d, time=%.3f, took %d us/step (avg. %d us/step)\n", - step, status.getSimulationTime(), (t - time) / 1000 / REPORT, (t - startTime) / 1000 / step); - time = t; - } - if (status.getSimulationTime() >= stopTime || step >= stopStep) { - System.out.printf("Stopping simulation, step=%d time=%.3f\n", step, status.getSimulationTime()); - status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, - status.getSimulationTime(), null)); - } - } - -} diff --git a/swing/resources/datafiles/examples/Roll-stabilized rocket.ork b/swing/resources/datafiles/examples/Roll-stabilized rocket.ork deleted file mode 100644 index f89cb43e5..000000000 Binary files a/swing/resources/datafiles/examples/Roll-stabilized rocket.ork and /dev/null differ diff --git a/swing/resources/datafiles/examples/Simulation Extension.ork b/swing/resources/datafiles/examples/Simulation Extension.ork new file mode 100644 index 000000000..6f8243e9d Binary files /dev/null and b/swing/resources/datafiles/examples/Simulation Extension.ork differ diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java index 8ff5dd1f1..e105af540 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java @@ -197,12 +197,12 @@ class SimulationOptionsPanel extends JPanel { final JButton addExtension = new SelectColorButton(trans.get("simedtdlg.SimExt.add")); - this.extensionMenu = getExtensionMenu(); + extensionMenu = getExtensionMenu(); addExtension.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent ev) { - extensionMenu.show(addExtension, 5, addExtension.getBounds().height); - } - }); + public void actionPerformed(ActionEvent ev) { + extensionMenu.show(addExtension, 5, addExtension.getBounds().height); + } + }); sub.add(addExtension, "growx, wrap 0"); currentExtensions = new JPanel(new MigLayout("fillx, gap 0 0, ins 0")); diff --git a/swing/src/net/sf/openrocket/simulation/extension/impl/AirStartConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/example/AirStartConfigurator.java similarity index 96% rename from swing/src/net/sf/openrocket/simulation/extension/impl/AirStartConfigurator.java rename to swing/src/net/sf/openrocket/simulation/extension/example/AirStartConfigurator.java index 8b7879526..d1a6a40df 100644 --- a/swing/src/net/sf/openrocket/simulation/extension/impl/AirStartConfigurator.java +++ b/swing/src/net/sf/openrocket/simulation/extension/example/AirStartConfigurator.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.simulation.extension.impl; +package net.sf.openrocket.simulation.extension.example; import javax.swing.JComponent; import javax.swing.JLabel; diff --git a/swing/src/net/sf/openrocket/simulation/extension/example/RollControlConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/example/RollControlConfigurator.java new file mode 100644 index 000000000..b14c80eef --- /dev/null +++ b/swing/src/net/sf/openrocket/simulation/extension/example/RollControlConfigurator.java @@ -0,0 +1,83 @@ +package net.sf.openrocket.simulation.extension.example; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + + +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.components.BasicSlider; +import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator; +import net.sf.openrocket.unit.UnitGroup; + +@Plugin +public class RollControlConfigurator extends AbstractSwingSimulationExtensionConfigurator { + private JPanel panel; + private RollControl extension; + + public RollControlConfigurator() { + super(RollControl.class); + } + + @Override + protected JComponent getConfigurationComponent(RollControl extension, Simulation simulation, JPanel panel) { + this.panel = panel; + this.extension = extension; + panel.add(new JLabel("Control FinSet Name:")); + + JTextField finSetName = new JTextField(); + finSetName.setText(extension.getControlFinName()); + + finSetName.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) {} + + @Override + public void focusLost(FocusEvent e) { + extension.setControlFinName(finSetName.getText()); + } + }); + + panel.add(finSetName, "growx, span3, wrap"); + + addRow("Start Time", "StartTime", UnitGroup.UNITS_SHORT_TIME, 0.0, 1200.0); + addRow("Desired Roll Rate", "SetPoint", UnitGroup.UNITS_ROLL, -10.0, 10.0); + addRow("Max Fin Turn Rate", "FinRate", UnitGroup.UNITS_ROLL, 0, 10); + addRow("Max Fin Angle", "MaxFinAngle", UnitGroup.UNITS_ANGLE, 0, 0.25); + + panel.add(new JLabel("PID parameters:"), "newline 0.25in, span4, wrap"); + panel.add(new JLabel("At M=0.3 KP oscillation threshold between 0.35 and 0.4. Good KI=3" ), "gapbefore indent, span4, wrap"); + panel.add(new JLabel("At M=0.6 KP oscillation threshold between 0.07 and 0.08 Good KI=2" ), "gapbefore indent, span4, wrap"); + panel.add(new JLabel("At M=0.9 KP oscillation threshold between 0.013 and 0.014 Good KI=0.5"), "gapbefore indent, span4, wrap"); + + addRow("KP (Proportional)", "KP", UnitGroup.UNITS_COEFFICIENT, 0.0, 0.02); + addRow("KI (Integrated)", "KI", UnitGroup.UNITS_COEFFICIENT, 0.0, 1.0); + + return panel; + } + + private void addRow(String prompt, String fieldName, UnitGroup units, double min, double max) { + panel.add(new JLabel(prompt + ":")); + + DoubleModel m = new DoubleModel(extension, fieldName, units, min, max); + + JSpinner spin = new JSpinner(m.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin)); + panel.add(spin, "w 65lp!"); + + UnitSelector unit = new UnitSelector(m); + panel.add(unit, "w 25"); + + BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000)); + panel.add(slider, "w 75lp, wrap"); + + } +} diff --git a/swing/src/net/sf/openrocket/simulation/extension/example/StopSimulationConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/example/StopSimulationConfigurator.java new file mode 100644 index 000000000..1918dabba --- /dev/null +++ b/swing/src/net/sf/openrocket/simulation/extension/example/StopSimulationConfigurator.java @@ -0,0 +1,52 @@ +package net.sf.openrocket.simulation.extension.example; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.SpinnerEditor; +import net.sf.openrocket.gui.adaptors.IntegerModel; +import net.sf.openrocket.gui.components.BasicSlider; +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator; +import net.sf.openrocket.unit.UnitGroup; + +@Plugin +public class StopSimulationConfigurator extends AbstractSwingSimulationExtensionConfigurator { + private JPanel panel; + private StopSimulation extension; + + public StopSimulationConfigurator() { + super(StopSimulation.class); + } + + @Override + protected JComponent getConfigurationComponent(StopSimulation extension, Simulation simulation, JPanel panel) { + this.panel = panel; + this.extension = extension; + + addRow("Report Rate", "ReportRate", 0, 1000); + addRow("Stop Step", "StopStep", 0, 50000); + addRow("Stop Time", "StopTime", 0, 1000); + + return panel; + } + + private void addRow(String prompt, String fieldName, int min, int max) { + panel.add(new JLabel(prompt + ":")); + + IntegerModel m = new IntegerModel(extension, fieldName, min, max); + + JSpinner spin = new JSpinner(m.getSpinnerModel()); + spin.setEditor(new SpinnerEditor(spin)); + panel.add(spin, "w 65lp!"); + + BasicSlider slider = new BasicSlider(m.getSliderModel()); + panel.add(slider, "w 75lp, wrap"); + } +} diff --git a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java index d49753dac..178d15cca 100644 --- a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java +++ b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java @@ -123,6 +123,7 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi @Override protected void close() { util.setTrustedScript(extension.getLanguage(), extension.getScript(), trusted.isSelected()); + super.close(); } private void setLanguage(String language) {