Added simulation of tumble recovery. Has pretty major FIXME which needs

addressing.
This commit is contained in:
kruland2607 2012-12-11 21:28:12 -06:00
parent fab85c2918
commit b99a704eb6
8 changed files with 1326 additions and 796 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1368,6 +1368,7 @@ FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT = Recovery device deployment
FlightEvent.Type.GROUND_HIT = Ground hit
FlightEvent.Type.SIMULATION_END = Simulation end
FlightEvent.Type.ALTITUDE = Altitude change
FlightEvent.Type.TUMBLE = Tumbling
! ThrustCurveMotorColumns
TCurveMotorCol.MANUFACTURER = Manufacturer

View File

@ -37,6 +37,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
//// Total motion vs. time
@ -50,6 +51,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
//// Flight side profile
@ -61,6 +63,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
//// Stability vs. time
@ -74,6 +77,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
//// Drag coefficients vs. Mach number
@ -98,6 +102,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
//// Angle of attack and orientation vs. time
@ -111,6 +116,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
//// Simulation time step and computation time
@ -123,6 +129,7 @@ public class PlotConfiguration implements Cloneable {
config.setEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, true);
config.setEvent(FlightEvent.Type.STAGE_SEPARATION, true);
config.setEvent(FlightEvent.Type.GROUND_HIT, true);
config.setEvent(FlightEvent.Type.TUMBLE, true);
configs.add(config);
DEFAULT_CONFIGURATIONS = configs.toArray(new PlotConfiguration[0]);

View File

@ -37,6 +37,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// TODO: MEDIUM: Allow selecting steppers
private SimulationStepper flightStepper = new RK4SimulationStepper();
private SimulationStepper landingStepper = new BasicLandingStepper();
private SimulationStepper tumbleStepper = new BasicTumbleStepper();
private SimulationStepper currentStepper;
@ -191,6 +192,20 @@ public class BasicEventSimulationEngine implements SimulationEngine {
(RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId));
}
}
// Check for Tumbling
// FIXME - need to test things like no longer stable.
if (status.isApogeeReached() && !status.isTumbling() ) {
int last_data_index = status.getFlightData().getLength() -1;
double cp = status.getFlightData().get(FlightDataType.TYPE_CP_LOCATION).get(last_data_index);
double cg = status.getFlightData().get(FlightDataType.TYPE_CG_LOCATION).get(last_data_index);
if( cg > cp ) {
addEvent( new FlightEvent(FlightEvent.Type.TUMBLE,status.getSimulationTime()));
}
status.setTumbling(true);
}
}
@ -479,6 +494,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
case ALTITUDE:
break;
case TUMBLE:
this.currentStepper = this.tumbleStepper;
this.status = currentStepper.initialize(status);
status.getFlightData().addEvent(event);
break;
}
}

View File

@ -0,0 +1,42 @@
package net.sf.openrocket.simulation;
import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
public class BasicTumbleStatus extends SimulationStatus {
private double tumbleCd;
public BasicTumbleStatus(Configuration configuration,
MotorInstanceConfiguration motorConfiguration,
SimulationConditions simulationConditions) {
super(configuration, motorConfiguration, simulationConditions);
computeTumbleCd();
}
public BasicTumbleStatus(SimulationStatus orig) {
super(orig);
if ( orig instanceof BasicTumbleStatus ) {
this.tumbleCd = ((BasicTumbleStatus) orig).tumbleCd;
}
}
public double getTumbleCd( ) {
return tumbleCd;
}
public void computeTumbleCd() {
// FIXME - probably want to compute the overall CD more accurately. Perhaps average
// CD over three AoA: 0, 90, 180. In any case, using barrowman to compute this Cd is
// completely wrong.
WarningSet warnings = new WarningSet();
FlightConditions cond = new FlightConditions(this.getConfiguration());
cond.setAOA(Math.PI);
AerodynamicForces forces = this.getSimulationConditions().getAerodynamicCalculator().getAerodynamicForces(this.getConfiguration(), cond, warnings);
tumbleCd = forces.getCD();
}
}

View File

@ -0,0 +1,138 @@
package net.sf.openrocket.simulation;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.WorldCoordinate;
public class BasicTumbleStepper extends AbstractSimulationStepper {
private static final double RECOVERY_TIME_STEP = 0.5;
@Override
public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
return new BasicTumbleStatus(status);
}
@Override
public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
double refArea = status.getConfiguration().getReferenceArea();
// Get the atmospheric conditions
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
//// Local wind speed and direction
Coordinate windSpeed = modelWindVelocity(status);
Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
// Get total CD
double mach = airSpeed.length() / atmosphere.getMachSpeed();
double totalCD = ((BasicTumbleStatus)status).getTumbleCd();
// Compute drag force
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
double dragForce = totalCD * dynP * refArea;
MassData massData = calculateMassData(status);
double mass = massData.getCG().weight;
// Compute drag acceleration
Coordinate linearAcceleration;
if (airSpeed.length() > 0.001) {
linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
} else {
linearAcceleration = Coordinate.NUL;
}
// Add effect of gravity
double gravity = modelGravity(status);
linearAcceleration = linearAcceleration.sub(0, 0, gravity);
// Add coriolis acceleration
Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
status.getRocketWorldPosition(), status.getRocketVelocity());
linearAcceleration = linearAcceleration.add(coriolisAcceleration);
// Select time step
double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
// Perform Euler integration
status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
status.setSimulationTime(status.getSimulationTime() + timeStep);
// Update the world coordinate
WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
status.setRocketWorldPosition(w);
// Store data
FlightDataBranch data = status.getFlightData();
boolean extra = status.getSimulationConditions().isCalculateExtras();
data.addPoint();
data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
if (extra) {
data.setValue(FlightDataType.TYPE_POSITION_XY,
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
data.setValue(FlightDataType.TYPE_VELOCITY_XY,
MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
double Re = airSpeed.length() *
status.getConfiguration().getLength() /
atmosphere.getKinematicViscosity();
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
}
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
data.setValue(FlightDataType.TYPE_GRAVITY, gravity);
if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
}
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
data.setValue(FlightDataType.TYPE_MASS, mass);
data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, 0.0); // Is this a reasonable assumption? Probably.
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
(System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);
}
}

View File

@ -72,7 +72,12 @@ public class FlightEvent implements Comparable<FlightEvent> {
* A change in altitude has occurred. Data is a <code>Pair<Double,Double></code>
* which contains the old and new altitudes.
*/
ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE"));
ALTITUDE(trans.get("FlightEvent.Type.ALTITUDE")),
/**
* The rocket begins to tumble.
*/
TUMBLE(trans.get("FlightEvent.Type.TUMBLE"));
private final String name;

View File

@ -69,6 +69,9 @@ public class SimulationStatus implements Monitorable {
/** Set to true when apogee has been detected. */
private boolean apogeeReached = false;
/** Set to true to indicate the rocket is tumbling. */
private boolean tumbling = false;
/** Contains a list of deployed recovery devices. */
private MonitorableSet<RecoveryDevice> deployedRecoveryDevices = new MonitorableSet<RecoveryDevice>();
@ -179,6 +182,7 @@ public class SimulationStatus implements Monitorable {
this.liftoff = orig.liftoff;
this.launchRodCleared = orig.launchRodCleared;
this.apogeeReached = orig.apogeeReached;
this.tumbling = orig.tumbling;
this.motorBurntOut = orig.motorBurntOut;
this.deployedRecoveryDevices.clear();
@ -380,6 +384,15 @@ public class SimulationStatus implements Monitorable {
}
public void setTumbling( boolean tumbling ) {
this.tumbling = tumbling;
this.modID++;
}
public boolean isTumbling() {
return tumbling;
}
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
return deployedRecoveryDevices;
}