Merge pull request #521 from JoePfeiffer/fix-361 -- Extend simulation until last motor burns out
Fix 361
This commit is contained in:
commit
64514ed514
@ -1693,7 +1693,7 @@ Warning.SUPERSONIC = Body calculations may not be entirely accurate at supersoni
|
||||
Warning.RECOVERY_LAUNCH_ROD = Recovery device device deployed while on the launch guide.
|
||||
Warning.RECOVERY_HIGH_SPEED = Recovery device deployment at high speed
|
||||
Warning.TUMBLE_UNDER_THRUST = Stage began to tumble under thrust.
|
||||
|
||||
Warning.EVENT_AFTER_LANDING = Flight Event occurred after landing:
|
||||
|
||||
! Scale dialog
|
||||
ScaleDialog.lbl.scaleRocket = Entire rocket
|
||||
|
@ -3,6 +3,7 @@ package net.sf.openrocket.aerodynamics;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
public abstract class Warning {
|
||||
@ -124,6 +125,41 @@ public abstract class Warning {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A <code>Warning</code> indicating flight events occurred after ground hit
|
||||
*
|
||||
*/
|
||||
public static class EventAfterLanding extends Warning {
|
||||
private FlightEvent event;
|
||||
|
||||
/**
|
||||
* Sole constructor. The argument is an event which has occurred after landing
|
||||
*
|
||||
* @param event the event that caused this warning
|
||||
*/
|
||||
public EventAfterLanding(FlightEvent _event) {
|
||||
this.event = _event;
|
||||
}
|
||||
|
||||
// I want a warning on every event that occurs after we land,
|
||||
// so severity of problem is clear to the user
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return trans.get("Warning.EVENT_AFTER_LANDING") + event.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceBy(Warning other) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MissingMotor extends Warning {
|
||||
|
||||
private Motor.Type type = null;
|
||||
@ -351,4 +387,6 @@ public abstract class Warning {
|
||||
|
||||
public static final Warning TUMBLE_UNDER_THRUST = new Other(trans.get("Warning.TUMBLE_UNDER_THRUST"));
|
||||
|
||||
public static final Warning EVENT_AFTER_LANDING = new Other(trans.get("Warning.EVENT_AFTER_LANDING"));
|
||||
|
||||
}
|
||||
|
@ -35,9 +35,10 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
private static final Logger log = LoggerFactory.getLogger(BasicEventSimulationEngine.class);
|
||||
|
||||
// TODO: MEDIUM: Allow selecting steppers
|
||||
private SimulationStepper flightStepper = new RK4SimulationStepper();
|
||||
private SimulationStepper flightStepper = new RK4SimulationStepper();
|
||||
private SimulationStepper landingStepper = new BasicLandingStepper();
|
||||
private SimulationStepper tumbleStepper = new BasicTumbleStepper();
|
||||
private SimulationStepper tumbleStepper = new BasicTumbleStepper();
|
||||
private SimulationStepper groundStepper = new GroundStepper();
|
||||
|
||||
// Constant holding 20 degrees in radians. This is the AOA condition
|
||||
// necessary to transition to tumbling.
|
||||
@ -107,8 +108,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
private FlightDataBranch simulateLoop() {
|
||||
|
||||
// Initialize the simulation
|
||||
currentStepper = flightStepper;
|
||||
// Initialize the simulation. We'll use the flight stepper unless we're already on the ground
|
||||
if (currentStatus.isLanded())
|
||||
currentStepper = groundStepper;
|
||||
else
|
||||
currentStepper = flightStepper;
|
||||
|
||||
currentStatus = currentStepper.initialize(currentStatus);
|
||||
|
||||
// Get originating position (in case listener has modified launch position)
|
||||
@ -138,16 +143,16 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
// Check for NaN values in the simulation status
|
||||
checkNaN();
|
||||
|
||||
// Add altitude event
|
||||
addEvent(new FlightEvent(FlightEvent.Type.ALTITUDE, currentStatus.getSimulationTime(),
|
||||
currentStatus.getConfiguration().getRocket(),
|
||||
new Pair<Double, Double>(oldAlt, currentStatus.getRocketPosition().z)));
|
||||
// If we haven't hit the ground, add altitude event
|
||||
if (!currentStatus.isLanded())
|
||||
addEvent(new FlightEvent(FlightEvent.Type.ALTITUDE, currentStatus.getSimulationTime(),
|
||||
currentStatus.getConfiguration().getRocket(),
|
||||
new Pair<Double, Double>(oldAlt, currentStatus.getRocketPosition().z)));
|
||||
|
||||
if (currentStatus.getRocketPosition().z > currentStatus.getMaxAlt()) {
|
||||
currentStatus.setMaxAlt(currentStatus.getRocketPosition().z);
|
||||
}
|
||||
|
||||
|
||||
// Position relative to start location
|
||||
Coordinate relativePosition = currentStatus.getRocketPosition().sub(origin);
|
||||
|
||||
@ -167,10 +172,10 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
} else {
|
||||
|
||||
// Check ground hit after liftoff
|
||||
if (currentStatus.getRocketPosition().z < 0) {
|
||||
currentStatus.setRocketPosition(currentStatus.getRocketPosition().setZ(0));
|
||||
if ((currentStatus.getRocketPosition().z < 0) && !currentStatus.isLanded()) {
|
||||
addEvent(new FlightEvent(FlightEvent.Type.GROUND_HIT, currentStatus.getSimulationTime()));
|
||||
addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, currentStatus.getSimulationTime()));
|
||||
|
||||
// addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, currentStatus.getSimulationTime()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -229,6 +234,9 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
}
|
||||
|
||||
// If I'm on the ground and have no events in the queue, I'm done
|
||||
if (currentStatus.isLanded() && currentStatus.getEventQueue().isEmpty())
|
||||
addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, currentStatus.getSimulationTime()));
|
||||
}
|
||||
|
||||
} catch (SimulationException e) {
|
||||
@ -255,6 +263,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
log.trace("Obtained event from queue: " + event.toString());
|
||||
log.trace("Remaining EventQueue = " + currentStatus.getEventQueue().toString());
|
||||
|
||||
// If I get an event other than ALTITUDE and SIMULATION_END after I'm on the ground, there's a problem
|
||||
if (currentStatus.isLanded() &&
|
||||
(event.getType() != FlightEvent.Type.ALTITUDE) &&
|
||||
(event.getType() != FlightEvent.Type.SIMULATION_END))
|
||||
currentStatus.getWarnings().add(new Warning.EventAfterLanding(event));
|
||||
|
||||
// Check for motor ignition events, add ignition events to queue
|
||||
for (MotorClusterState state : currentStatus.getActiveMotors() ){
|
||||
if( state.testForIgnition(event )){
|
||||
@ -391,7 +405,6 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
//log.debug( " adding EJECTION_CHARGE event for motor "+motorState.getMotor().getDesignation()+" on stage "+stage.getStageNumber()+": "+stage.getName());
|
||||
log.debug( " detected Motor Burnout for motor "+motorState.getMotor().getDesignation()+"@ "+event.getTime()+" on stage "+stage.getStageNumber()+": "+stage.getName());
|
||||
|
||||
|
||||
double delay = motorState.getEjectionDelay();
|
||||
if ( motorState.hasEjectionCharge() ){
|
||||
addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, currentStatus.getSimulationTime() + delay,
|
||||
@ -479,14 +492,22 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
currentStatus.getFlightData().setTimeToOptimumAltitude(coastStatus.getTimeToApogee());
|
||||
}
|
||||
|
||||
this.currentStepper = this.landingStepper;
|
||||
this.currentStatus = currentStepper.initialize(currentStatus);
|
||||
// switch to landing stepper (unless we're already on the ground)
|
||||
if (!currentStatus.isLanded()) {
|
||||
currentStepper = landingStepper;
|
||||
currentStatus = currentStepper.initialize(currentStatus);
|
||||
}
|
||||
|
||||
currentStatus.getFlightData().addEvent(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case GROUND_HIT:
|
||||
currentStatus.setLanded(true);
|
||||
|
||||
currentStepper = groundStepper;
|
||||
currentStatus = currentStepper.initialize(currentStatus);
|
||||
|
||||
currentStatus.getFlightData().addEvent(event);
|
||||
break;
|
||||
|
||||
@ -500,8 +521,10 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
break;
|
||||
|
||||
case TUMBLE:
|
||||
this.currentStepper = this.tumbleStepper;
|
||||
this.currentStatus = currentStepper.initialize(currentStatus);
|
||||
if (!currentStatus.isLanded()) {
|
||||
currentStepper = tumbleStepper;
|
||||
currentStatus = currentStepper.initialize(currentStatus);
|
||||
}
|
||||
currentStatus.getFlightData().addEvent(event);
|
||||
break;
|
||||
}
|
||||
@ -538,7 +561,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
/**
|
||||
* Return the next flight event to handle, or null if no more events should be handled.
|
||||
* This method jumps the simulation time forward in case no motors have been ignited.
|
||||
* This method jumps the simulation time forward in case no motors have been ignited
|
||||
* The flight event is removed from the event queue.
|
||||
*
|
||||
* @return the flight event to handle, or null
|
||||
@ -550,7 +573,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
return null;
|
||||
|
||||
// Jump to event if no motors have been ignited
|
||||
if (!currentStatus.isMotorIgnited() && event.getTime() > currentStatus.getSimulationTime()) {
|
||||
if (!currentStatus.isMotorIgnited() &&
|
||||
event.getTime() > currentStatus.getSimulationTime()) {
|
||||
currentStatus.setSimulationTime(event.getTime());
|
||||
}
|
||||
if (event.getTime() <= currentStatus.getSimulationTime()) {
|
||||
|
85
core/src/net/sf/openrocket/simulation/GroundStepper.java
Normal file
85
core/src/net/sf/openrocket/simulation/GroundStepper.java
Normal file
@ -0,0 +1,85 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
||||
public class GroundStepper extends AbstractSimulationStepper {
|
||||
private static final Logger log = LoggerFactory.getLogger(GroundStepper.class);
|
||||
|
||||
@Override
|
||||
public SimulationStatus initialize(SimulationStatus original) {
|
||||
log.trace("initializing GroundStepper");
|
||||
SimulationStatus status = new SimulationStatus(original);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void step(SimulationStatus status, double timeStep) throws SimulationException {
|
||||
log.trace("step: position=" + status.getRocketPosition() + ", velocity=" + status.getRocketVelocity());
|
||||
|
||||
status.setRocketVelocity(Coordinate.ZERO);
|
||||
status.setRocketRotationVelocity(Coordinate.ZERO);
|
||||
status.setRocketPosition(status.getRocketPosition().setZ(0));
|
||||
|
||||
// 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, 0.0);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, 0.0);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Double.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
|
||||
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
|
||||
data.setValue(FlightDataType.TYPE_GRAVITY, modelGravity(status));
|
||||
|
||||
data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, 0.0);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
|
||||
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, 0.0);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, 0.0);
|
||||
data.setValue(FlightDataType.TYPE_MACH_NUMBER, 0.0);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_MASS, calculateStructureMass(status).getMass());
|
||||
data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, 0.0); // Is this a reasonable assumption? Probably.
|
||||
|
||||
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0.0);
|
||||
data.setValue(FlightDataType.TYPE_DRAG_FORCE, 0.0);
|
||||
|
||||
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, modelWindVelocity(status).length());
|
||||
|
||||
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
|
||||
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);
|
||||
|
||||
status.setSimulationTime(status.getSimulationTime() + timeStep);
|
||||
}
|
||||
}
|
@ -71,6 +71,9 @@ public class SimulationStatus implements Monitorable {
|
||||
/** Set to true to indicate the rocket is tumbling. */
|
||||
private boolean tumbling = false;
|
||||
|
||||
/** Set to true to indicate rocket has landed */
|
||||
private boolean landed = false;
|
||||
|
||||
/** Contains a list of deployed recovery devices. */
|
||||
private MonitorableSet<RecoveryDevice> deployedRecoveryDevices = new MonitorableSet<RecoveryDevice>();
|
||||
|
||||
@ -175,6 +178,7 @@ public class SimulationStatus implements Monitorable {
|
||||
this.launchRodCleared = orig.launchRodCleared;
|
||||
this.apogeeReached = orig.apogeeReached;
|
||||
this.tumbling = orig.tumbling;
|
||||
this.landed = orig.landed;
|
||||
|
||||
this.configuration.copyStages(orig.configuration);
|
||||
|
||||
@ -393,6 +397,15 @@ public class SimulationStatus implements Monitorable {
|
||||
return tumbling;
|
||||
}
|
||||
|
||||
public void setLanded(boolean landed) {
|
||||
this.landed = landed;
|
||||
this.modID++;
|
||||
}
|
||||
|
||||
public boolean isLanded() {
|
||||
return landed;
|
||||
}
|
||||
|
||||
public double getMaxAlt() {
|
||||
return maxAlt;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user