From 2e015444de25266a10c77397ae33bca92457ab10 Mon Sep 17 00:00:00 2001 From: plaa Date: Sun, 26 Jun 2011 16:01:26 +0000 Subject: [PATCH] simulation listener enhancements git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@131 180e2498-e6e9-4542-8430-84ac67f01cd8 --- ChangeLog | 8 +++ src-extra/AirStart.java | 21 ++++++ .../BasicEventSimulationEngine.java | 23 +++++-- .../simulation/GUISimulationConditions.java | 2 +- .../listeners/example/AirStart.java | 38 ++++++++++ .../RollControlListener.java | 69 ++++++++++++------- 6 files changed, 129 insertions(+), 32 deletions(-) create mode 100644 src-extra/AirStart.java create mode 100644 src/net/sf/openrocket/simulation/listeners/example/AirStart.java rename src/net/sf/openrocket/simulation/listeners/{haisu => example}/RollControlListener.java (54%) diff --git a/ChangeLog b/ChangeLog index 78c5cf60..467384ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2011-06-26 Sampo Niskanen + + * [BUG] Original rocket was modified when sim.listener modified rocket + +2011-06-18 Sampo Niskanen + + * Merged l10n branch into trunk, initial l10n support + 2011-06-10 Sampo Niskanen * Released version 1.1.5 diff --git a/src-extra/AirStart.java b/src-extra/AirStart.java new file mode 100644 index 00000000..9334c0d7 --- /dev/null +++ b/src-extra/AirStart.java @@ -0,0 +1,21 @@ +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; +import net.sf.openrocket.util.Coordinate; + +/** + * Simulation listener that launches a rocket from a specific altitude. + */ +public class AirStart extends AbstractSimulationListener { + + /** Launch altitude */ + private static final double ALTITUDE = 1000.0; + + @Override + public void startSimulation(SimulationStatus status) throws SimulationException { + Coordinate position = status.getRocketPosition(); + position = position.add(0, 0, ALTITUDE); + status.setRocketPosition(position); + } + +} diff --git a/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 2740ca43..21ccb266 100644 --- a/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -61,8 +61,12 @@ public class BasicEventSimulationEngine implements SimulationEngine { SimulationListenerHelper.fireStartSimulation(status); + // Get originating position (in case listener has modified launch position) + Coordinate origin = status.getRocketPosition(); + Coordinate originVelocity = status.getRocketVelocity(); try { + double maxAlt = Double.NEGATIVE_INFINITY; // Start the simulation while (handleEvents()) { @@ -91,17 +95,24 @@ public class BasicEventSimulationEngine implements SimulationEngine { status.getConfiguration().getRocket(), new Pair(oldAlt, status.getRocketPosition().z))); + if (status.getRocketPosition().z > maxAlt) { + maxAlt = status.getRocketPosition().z; + } + + // Position relative to start location + Coordinate relativePosition = status.getRocketPosition().sub(origin); + // Add appropriate events if (!status.isLiftoff()) { // Avoid sinking into ground before liftoff - if (status.getRocketPosition().z < 0) { - status.setRocketPosition(Coordinate.NUL); - status.setRocketVelocity(Coordinate.NUL); + if (relativePosition.z < 0) { + status.setRocketPosition(origin); + status.setRocketVelocity(originVelocity); } // Detect lift-off - if (status.getRocketPosition().z > 0.01) { + if (relativePosition.z > 0.02) { addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, status.getSimulationTime())); } @@ -118,13 +129,13 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Check for launch guide clearance if (!status.isLaunchRodCleared() && - status.getRocketPosition().length() > status.getSimulationConditions().getLaunchRodLength()) { + relativePosition.length() > status.getSimulationConditions().getLaunchRodLength()) { addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null)); } // Check for apogee - if (!status.isApogeeReached() && status.getRocketPosition().z < oldAlt - 0.001) { + if (!status.isApogeeReached() && status.getRocketPosition().z < maxAlt - 0.01) { addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(), status.getConfiguration().getRocket())); } diff --git a/src/net/sf/openrocket/simulation/GUISimulationConditions.java b/src/net/sf/openrocket/simulation/GUISimulationConditions.java index 837b8c10..7ffc2dd4 100644 --- a/src/net/sf/openrocket/simulation/GUISimulationConditions.java +++ b/src/net/sf/openrocket/simulation/GUISimulationConditions.java @@ -436,7 +436,7 @@ public class GUISimulationConditions implements ChangeSource, Cloneable { public SimulationConditions toSimulationConditions() { SimulationConditions conditions = new SimulationConditions(); - conditions.setRocket(getRocket()); + conditions.setRocket((Rocket) getRocket().copy()); conditions.setMotorConfigurationID(getMotorConfigurationID()); conditions.setLaunchRodLength(getLaunchRodLength()); conditions.setLaunchRodAngle(getLaunchRodAngle()); diff --git a/src/net/sf/openrocket/simulation/listeners/example/AirStart.java b/src/net/sf/openrocket/simulation/listeners/example/AirStart.java new file mode 100644 index 00000000..97ce93a2 --- /dev/null +++ b/src/net/sf/openrocket/simulation/listeners/example/AirStart.java @@ -0,0 +1,38 @@ +package net.sf.openrocket.simulation.listeners.example; + +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; +import net.sf.openrocket.util.Coordinate; + +/** + * Simulation listener that launches a rocket from a specific altitude. + *

+ * The altitude is read from the system property "openrocket.airstart.altitude" + * if defined, otherwise a default altitude of 1000 meters is used. + */ +public class AirStart extends AbstractSimulationListener { + + /** Default launch altitude */ + private static final double DEFAULT_ALTITUDE = 1000.0; + + @Override + public void startSimulation(SimulationStatus status) throws SimulationException { + + // Get the launch altitude + double altitude; + String arg = System.getProperty("openrocket.airstart.altitude"); + try { + altitude = Double.parseDouble(arg); + } catch (RuntimeException e) { + altitude = DEFAULT_ALTITUDE; + } + + // Modify launch position + Coordinate position = status.getRocketPosition(); + position = position.add(0, 0, altitude); + status.setRocketPosition(position); + + } + +} diff --git a/src/net/sf/openrocket/simulation/listeners/haisu/RollControlListener.java b/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java similarity index 54% rename from src/net/sf/openrocket/simulation/listeners/haisu/RollControlListener.java rename to src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java index b29e7d99..80993218 100644 --- a/src/net/sf/openrocket/simulation/listeners/haisu/RollControlListener.java +++ b/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java @@ -1,10 +1,13 @@ -package net.sf.openrocket.simulation.listeners.haisu; +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; /** @@ -15,15 +18,29 @@ import net.sf.openrocket.util.MathUtil; */ public class RollControlListener extends AbstractSimulationListener { - private static final double DELTA_T = 0.01; + // 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", + 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; - private static final double TURNRATE = 10 * Math.PI / 180; // per second + // 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 @@ -32,58 +49,63 @@ public class RollControlListener extends AbstractSimulationListener { private static final double KI = 0.2; - private static final double MAX_ANGLE = 15 * Math.PI / 180; - - private double rollrate; + private double prevTime = 0; private double intState = 0; private double finPosition = 0; - public RollControlListener() { - } - + @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) { + public void postStep(SimulationStatus status) throws SimulationException { - if (status.getSimulationTime() < START_TIME) + // Activate PID controller only after a specific time + if (status.getSimulationTime() < START_TIME) { + prevTime = status.getSimulationTime(); return; + } - // PID controller + // Find the fin set named CONTROL FinSet finset = null; for (RocketComponent c : status.getConfiguration()) { - if ((c instanceof FinSet) && (c.getName().equals("CONTROL"))) { + if ((c instanceof FinSet) && (c.getName().equals(CONTROL_FIN_NAME))) { finset = (FinSet) c; break; } } if (finset == null) { - throw new RuntimeException("CONTROL fin not found"); + throw new SimulationException("A fin set with name '" + CONTROL_FIN_NAME + "' was not found"); } - double error = SETPOINT - rollrate; + // Determine time step + double deltaT = status.getSimulationTime() - prevTime; + prevTime = status.getSimulationTime(); - error = Math.signum(error) * error * error; //// pow2(error) + // PID controller + double error = SETPOINT - rollrate; double p = KP * error; - intState += error * DELTA_T; + 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()); @@ -91,19 +113,16 @@ public class RollControlListener extends AbstractSimulationListener { } + // Limit the fin turn rate if (finPosition < value) { - finPosition = Math.min(finPosition + TURNRATE * DELTA_T, value); + finPosition = Math.min(finPosition + TURNRATE * deltaT, value); } else { - finPosition = Math.max(finPosition - TURNRATE * DELTA_T, value); - } - - if (MathUtil.equals(status.getSimulationTime() * 10, Math.rint(status.getSimulationTime() * 10))) { - System.err.printf("t=%.3f angle=%.1f current=%.1f\n", status.getSimulationTime(), - value * 180 / Math.PI, finPosition * 180 / Math.PI); + 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); } - } -- 2.30.2