From: plaa Date: Sun, 26 Jun 2011 16:01:26 +0000 (+0000) Subject: simulation listener enhancements X-Git-Tag: upstream/1.1.6^2~12 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=2e015444de25266a10c77397ae33bca92457ab10;p=debian%2Fopenrocket simulation listener enhancements git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@131 180e2498-e6e9-4542-8430-84ac67f01cd8 --- 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/example/RollControlListener.java b/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java new file mode 100644 index 00000000..80993218 --- /dev/null +++ b/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java @@ -0,0 +1,128 @@ +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", + 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()) { + 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/src/net/sf/openrocket/simulation/listeners/haisu/RollControlListener.java b/src/net/sf/openrocket/simulation/listeners/haisu/RollControlListener.java deleted file mode 100644 index b29e7d99..00000000 --- a/src/net/sf/openrocket/simulation/listeners/haisu/RollControlListener.java +++ /dev/null @@ -1,109 +0,0 @@ -package net.sf.openrocket.simulation.listeners.haisu; - -import net.sf.openrocket.aerodynamics.FlightConditions; -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.simulation.SimulationStatus; -import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; -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 { - - private static final double DELTA_T = 0.01; - private static final double START_TIME = 0.5; - - private static final double SETPOINT = 0.0; - - private static final double TURNRATE = 10 * Math.PI / 180; // per second - - - /* - * 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 static final double MAX_ANGLE = 15 * Math.PI / 180; - - - - - private double rollrate; - - private double intState = 0; - - private double finPosition = 0; - - - public RollControlListener() { - } - - @Override - public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) { - rollrate = flightConditions.getRollRate(); - return null; - } - - @Override - public void postStep(SimulationStatus status) { - - if (status.getSimulationTime() < START_TIME) - return; - - // PID controller - FinSet finset = null; - for (RocketComponent c : status.getConfiguration()) { - if ((c instanceof FinSet) && (c.getName().equals("CONTROL"))) { - finset = (FinSet) c; - break; - } - } - if (finset == null) { - throw new RuntimeException("CONTROL fin not found"); - } - - - double error = SETPOINT - rollrate; - - - error = Math.signum(error) * error * error; //// pow2(error) - - double p = KP * error; - intState += error * DELTA_T; - double i = KI * intState; - - double value = p + i; - - - 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); - } - - - if (finPosition < value) { - finPosition = Math.min(finPosition + TURNRATE * DELTA_T, 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); - } - - finset.setCantAngle(finPosition); - - } - -}