simulation listener enhancements

This commit is contained in:
Sampo Niskanen 2011-06-26 16:01:26 +00:00
parent 159dc25c02
commit 53d5f2163d
6 changed files with 129 additions and 32 deletions

View File

@ -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 2011-06-10 Sampo Niskanen
* Released version 1.1.5 * Released version 1.1.5

21
src-extra/AirStart.java Normal file
View File

@ -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);
}
}

View File

@ -61,8 +61,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
SimulationListenerHelper.fireStartSimulation(status); SimulationListenerHelper.fireStartSimulation(status);
// Get originating position (in case listener has modified launch position)
Coordinate origin = status.getRocketPosition();
Coordinate originVelocity = status.getRocketVelocity();
try { try {
double maxAlt = Double.NEGATIVE_INFINITY;
// Start the simulation // Start the simulation
while (handleEvents()) { while (handleEvents()) {
@ -91,17 +95,24 @@ public class BasicEventSimulationEngine implements SimulationEngine {
status.getConfiguration().getRocket(), status.getConfiguration().getRocket(),
new Pair<Double, Double>(oldAlt, status.getRocketPosition().z))); 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 // Add appropriate events
if (!status.isLiftoff()) { if (!status.isLiftoff()) {
// Avoid sinking into ground before liftoff // Avoid sinking into ground before liftoff
if (status.getRocketPosition().z < 0) { if (relativePosition.z < 0) {
status.setRocketPosition(Coordinate.NUL); status.setRocketPosition(origin);
status.setRocketVelocity(Coordinate.NUL); status.setRocketVelocity(originVelocity);
} }
// Detect lift-off // Detect lift-off
if (status.getRocketPosition().z > 0.01) { if (relativePosition.z > 0.02) {
addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, status.getSimulationTime())); addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, status.getSimulationTime()));
} }
@ -118,13 +129,13 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for launch guide clearance // Check for launch guide clearance
if (!status.isLaunchRodCleared() && if (!status.isLaunchRodCleared() &&
status.getRocketPosition().length() > status.getSimulationConditions().getLaunchRodLength()) { relativePosition.length() > status.getSimulationConditions().getLaunchRodLength()) {
addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null)); addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null));
} }
// Check for apogee // 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(), addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(),
status.getConfiguration().getRocket())); status.getConfiguration().getRocket()));
} }

View File

@ -436,7 +436,7 @@ public class GUISimulationConditions implements ChangeSource, Cloneable {
public SimulationConditions toSimulationConditions() { public SimulationConditions toSimulationConditions() {
SimulationConditions conditions = new SimulationConditions(); SimulationConditions conditions = new SimulationConditions();
conditions.setRocket(getRocket()); conditions.setRocket((Rocket) getRocket().copy());
conditions.setMotorConfigurationID(getMotorConfigurationID()); conditions.setMotorConfigurationID(getMotorConfigurationID());
conditions.setLaunchRodLength(getLaunchRodLength()); conditions.setLaunchRodLength(getLaunchRodLength());
conditions.setLaunchRodAngle(getLaunchRodAngle()); conditions.setLaunchRodAngle(getLaunchRodAngle());

View File

@ -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.
* <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);
}
}

View File

@ -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.aerodynamics.FlightConditions;
import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
/** /**
@ -15,15 +18,29 @@ import net.sf.openrocket.util.MathUtil;
*/ */
public class RollControlListener extends AbstractSimulationListener { 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; private static final double START_TIME = 0.5;
// Desired roll rate (rad/sec)
private static final double SETPOINT = 0.0; 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.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.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 * 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 KI = 0.2;
private static final double MAX_ANGLE = 15 * Math.PI / 180;
private double rollrate; private double rollrate;
private double prevTime = 0;
private double intState = 0; private double intState = 0;
private double finPosition = 0; private double finPosition = 0;
public RollControlListener() {
}
@Override @Override
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) { public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) {
// Store the current roll rate for later use
rollrate = flightConditions.getRollRate(); rollrate = flightConditions.getRollRate();
return null; return null;
} }
@Override @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; return;
}
// PID controller // Find the fin set named CONTROL
FinSet finset = null; FinSet finset = null;
for (RocketComponent c : status.getConfiguration()) { 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; finset = (FinSet) c;
break; break;
} }
} }
if (finset == null) { if (finset == null) {
throw new RuntimeException("CONTROL fin not found"); 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 error = SETPOINT - rollrate;
error = Math.signum(error) * error * error; //// pow2(error)
double p = KP * error; double p = KP * error;
intState += error * DELTA_T; intState += error * deltaT;
double i = KI * intState; double i = KI * intState;
double value = p + i; double value = p + i;
// Clamp the fin angle between -MAX_ANGLE and MAX_ANGLE
if (Math.abs(value) > MAX_ANGLE) { if (Math.abs(value) > MAX_ANGLE) {
System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n", System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n",
value * 180 / Math.PI, status.getSimulationTime()); value * 180 / Math.PI, status.getSimulationTime());
@ -91,19 +113,16 @@ public class RollControlListener extends AbstractSimulationListener {
} }
// Limit the fin turn rate
if (finPosition < value) { if (finPosition < value) {
finPosition = Math.min(finPosition + TURNRATE * DELTA_T, value); finPosition = Math.min(finPosition + TURNRATE * deltaT, value);
} else { } else {
finPosition = Math.max(finPosition - TURNRATE * DELTA_T, value); finPosition = Math.max(finPosition - TURNRATE * deltaT, 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);
} }
// Set the control fin cant and store the data
finset.setCantAngle(finPosition); finset.setCantAngle(finPosition);
status.getFlightData().setValue(FIN_CANT_TYPE, finPosition);
} }
} }