+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
--- /dev/null
+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);
+ }
+
+}
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()) {
status.getConfiguration().getRocket(),
new Pair<Double, Double>(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()));
}
// 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()));
}
public SimulationConditions toSimulationConditions() {
SimulationConditions conditions = new SimulationConditions();
- conditions.setRocket(getRocket());
+ conditions.setRocket((Rocket) getRocket().copy());
conditions.setMotorConfigurationID(getMotorConfigurationID());
conditions.setLaunchRodLength(getLaunchRodLength());
conditions.setLaunchRodAngle(getLaunchRodAngle());
--- /dev/null
+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.
+ * <p>
+ * 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);
+
+ }
+
+}
--- /dev/null
+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 <sampo.niskanen@iki.fi>
+ */
+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);
+
+ }
+}
+++ /dev/null
-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 <sampo.niskanen@iki.fi>
- */
-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);
-
- }
-
-}