Merge pull request #2457 from JoePfeiffer/allow-sustainer-tumble
Allow sustainer tumble before apogee
This commit is contained in:
commit
7c246e7144
@ -258,21 +258,16 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
// Check for Tumbling
|
||||
// Conditions for transition are:
|
||||
// apogee reached (if sustainer stage)
|
||||
// and is not already tumbling
|
||||
// is not already tumbling
|
||||
// and not stable (cg > cp)
|
||||
// and aoa > AOA_TUMBLE_CONDITION threshold
|
||||
// and thrust < THRUST_TUMBLE_CONDITION threshold
|
||||
|
||||
if (!currentStatus.isTumbling()) {
|
||||
final double cp = currentStatus.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION);
|
||||
final double cg = currentStatus.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION);
|
||||
final double aoa = currentStatus.getFlightData().getLast(FlightDataType.TYPE_AOA);
|
||||
|
||||
final boolean wantToTumble = (cg > cp && aoa > AOA_TUMBLE_CONDITION);
|
||||
final boolean isSustainer = currentStatus.getConfiguration().isStageActive(0);
|
||||
final boolean isApogee = currentStatus.isApogeeReached();
|
||||
if (wantToTumble && (isApogee || !isSustainer)) {
|
||||
if (cg > cp && aoa > AOA_TUMBLE_CONDITION) {
|
||||
currentStatus.addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, currentStatus.getSimulationTime()));
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,14 @@ import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.document.OpenRocketDocumentFactory;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.document.StorageOptions;
|
||||
import net.sf.openrocket.file.openrocket.OpenRocketSaver;
|
||||
import net.sf.openrocket.logging.ErrorSet;
|
||||
import net.sf.openrocket.logging.WarningSet;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.material.Material.Type;
|
||||
import net.sf.openrocket.motor.Manufacturer;
|
||||
import net.sf.openrocket.motor.IgnitionEvent;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
@ -137,6 +139,45 @@ public class TestRockets {
|
||||
.build();
|
||||
}
|
||||
|
||||
// This function is used for unit, integration tests, DO NOT CHANGE (without updating tests).
|
||||
private static Motor generateMotor_A10_13mm(){
|
||||
return new ThrustCurveMotor.Builder()
|
||||
.setManufacturer(Manufacturer.getManufacturer("Estes"))
|
||||
.setDesignation("A10")
|
||||
.setDescription(" SU Black Powder")
|
||||
.setCaseInfo("SU 13.0x45.0")
|
||||
.setMotorType(Motor.Type.SINGLE)
|
||||
.setStandardDelays(new double[] {0,3})
|
||||
.setDiameter(0.013)
|
||||
.setLength(0.045)
|
||||
.setTimePoints(new double[] {0.0, 0.026, 0.055, 0.093, 0.124, 0.146, 0.166, 0.179, 0.194, 0.203, 0.209, 0.225, 0.26, 0.333, 0.456, 0.575, 0.663, 0.76, 0.811, 0.828, 0.85})
|
||||
. setThrustPoints(new double[] {0.0, 0.478, 1.919, 4.513, 8.165, 10.956, 12.64, 11.046, 7.966, 6.042, 3.154, 1.421, 1.225, 1.41, 1.206, 1.195, 1.282, 1.273, 1.268, 0.689, 0.0})
|
||||
.setCGPoints(new Coordinate[] {
|
||||
new Coordinate(0.0225, 0, 0, 3.8),
|
||||
new Coordinate(0.0225, 0, 0, 3.78818),
|
||||
new Coordinate(0.0225, 0, 0, 3.72207),
|
||||
new Coordinate(0.0225, 0, 0, 3.48963),
|
||||
new Coordinate(0.0225, 0, 0, 3.11587),
|
||||
new Coordinate(0.0225, 0, 0, 2.71582),
|
||||
new Coordinate(0.0225, 0, 0, 2.26703),
|
||||
new Coordinate(0.0225, 0, 0, 1.97419),
|
||||
new Coordinate(0.0225, 0, 0, 1.70299),
|
||||
new Coordinate(0.0225, 0, 0, 1.58309),
|
||||
new Coordinate(0.0225, 0, 0, 1.53062),
|
||||
new Coordinate(0.0225, 0, 0, 1.46101),
|
||||
new Coordinate(0.0225, 0, 0, 1.37293),
|
||||
new Coordinate(0.0225, 0, 0, 1.19),
|
||||
new Coordinate(0.0225, 0, 0, 0.884002),
|
||||
new Coordinate(0.0225, 0, 0, 0.612283),
|
||||
new Coordinate(0.0225, 0, 0, 0.404987),
|
||||
new Coordinate(0.0225, 0, 0, 0.169296),
|
||||
new Coordinate(0.0225, 0, 0, 0.0460542),
|
||||
new Coordinate(0.0225, 0, 0, 0.0144153),
|
||||
new Coordinate(0.0225, 0, 0, 0.0)})
|
||||
.setDigest("digest A10 test")
|
||||
.build();
|
||||
}
|
||||
|
||||
// This function is used for unit, integration tests, DO NOT CHANGE (without updating tests).
|
||||
private static Motor generateMotor_B4_18mm(){
|
||||
return new ThrustCurveMotor.Builder()
|
||||
@ -1131,7 +1172,123 @@ public class TestRockets {
|
||||
|
||||
rocket.getChild(1).getChild(0).addChild(finSet);
|
||||
}
|
||||
|
||||
// This is a rocket with two axial stages and side boosters for multi-stage event tests.
|
||||
// It's like a Falcon 9 Heavy (see above), but vastly simplified so it can be checked by hand
|
||||
// if needed, and (more importantly) aerodynamically stable. It lacks a lot of the internal
|
||||
// structure that's required by a real rocket
|
||||
public static Rocket makeMultiStageEventTestRocket() {
|
||||
|
||||
Rocket rocket = new Rocket();
|
||||
|
||||
FlightConfigurationId selFCID = rocket.createFlightConfiguration(new FlightConfigurationId()).getFlightConfigurationID();
|
||||
|
||||
final double THICKNESS = 0.002;
|
||||
final double CORE_RADIUS = 0.01;
|
||||
final int NUM_FINS = 4;
|
||||
final double ROOT_CHORD = 0.04;
|
||||
final double TIP_CHORD = 0.02;
|
||||
final double SWEEP = 0.015;
|
||||
final double HEIGHT = 0.03;
|
||||
|
||||
// Sustainer
|
||||
AxialStage sustainer = new AxialStage();
|
||||
sustainer.setName("Sustainer");
|
||||
rocket.addChild(sustainer);
|
||||
{
|
||||
final double NC_LENGTH = 0.1;
|
||||
NoseCone noseCone = new NoseCone(Transition.Shape.OGIVE, NC_LENGTH, CORE_RADIUS);
|
||||
noseCone.setName("Sustainer Nose Cone");
|
||||
sustainer.addChild(noseCone);
|
||||
|
||||
final double BT_LENGTH = 0.2;
|
||||
BodyTube bodyTube = new BodyTube(BT_LENGTH, CORE_RADIUS, THICKNESS);
|
||||
bodyTube.setName("Sustainer Body Tube");
|
||||
bodyTube.setMotorMount(true);
|
||||
sustainer.addChild(bodyTube);
|
||||
|
||||
MotorConfiguration motorConfig = new MotorConfiguration(bodyTube, selFCID);
|
||||
motorConfig.setMotor(TestRockets.generateMotor_C6_18mm());
|
||||
motorConfig.setEjectionDelay(5.0);
|
||||
motorConfig.setIgnitionEvent(IgnitionEvent.BURNOUT);
|
||||
bodyTube.setMotorConfig(motorConfig, selFCID);
|
||||
|
||||
final double CHUTE_DIAM = 0.3;
|
||||
Parachute parachute = new Parachute();
|
||||
parachute.setName("Sustainer Parachute");
|
||||
parachute.setDiameter(CHUTE_DIAM);
|
||||
bodyTube.addChild(parachute);
|
||||
|
||||
final double POSITION = 0.0;
|
||||
TrapezoidFinSet finSet = new TrapezoidFinSet(NUM_FINS, ROOT_CHORD, TIP_CHORD, SWEEP, HEIGHT);
|
||||
finSet.setName("Sustainer Fin Set");
|
||||
finSet.setAxialMethod(AxialMethod.BOTTOM);
|
||||
finSet.setAxialOffset(POSITION);
|
||||
bodyTube.addChild(finSet);
|
||||
}
|
||||
|
||||
// Center Booster
|
||||
AxialStage centerBooster = new AxialStage();
|
||||
centerBooster.setName("Center Booster");
|
||||
rocket.addChild(centerBooster);
|
||||
{
|
||||
final double BT_LENGTH = 0.09;
|
||||
|
||||
BodyTube bodyTube = new BodyTube(BT_LENGTH, CORE_RADIUS, THICKNESS);
|
||||
bodyTube.setName("Center Booster Body Tube");
|
||||
bodyTube.setMotorMount(true);
|
||||
centerBooster.addChild(bodyTube);
|
||||
|
||||
MotorConfiguration motorConfig = new MotorConfiguration(bodyTube, selFCID);
|
||||
motorConfig.setMotor(TestRockets.generateMotor_C6_18mm());
|
||||
motorConfig.setEjectionDelay(0.0);
|
||||
motorConfig.setIgnitionEvent(IgnitionEvent.LAUNCH);
|
||||
motorConfig.setIgnitionDelay(0.01);
|
||||
bodyTube.setMotorConfig(motorConfig, selFCID);
|
||||
|
||||
final double POSITION = -0.015;
|
||||
TrapezoidFinSet finSet = new TrapezoidFinSet(NUM_FINS, ROOT_CHORD, TIP_CHORD, SWEEP, HEIGHT);
|
||||
finSet.setName("Center Booster Fin Set");
|
||||
finSet.setAxialMethod(AxialMethod.BOTTOM);
|
||||
finSet.setAxialOffset(POSITION);
|
||||
bodyTube.addChild(finSet);
|
||||
}
|
||||
|
||||
// Side Boosters
|
||||
final double SIDE_RADIUS = 0.007;
|
||||
ParallelStage sideBoosters = new ParallelStage(2);
|
||||
sideBoosters.setName("Side boosters");
|
||||
rocket.getChild(1).getChild(0).addChild(sideBoosters);
|
||||
sideBoosters.setAngleOffset(Math.PI/4);
|
||||
{
|
||||
final double NC_LENGTH = 0.05;
|
||||
NoseCone noseCone = new NoseCone(Transition.Shape.OGIVE, NC_LENGTH, SIDE_RADIUS);
|
||||
noseCone.setName("Side Booster Nose Cones");
|
||||
sideBoosters.addChild(noseCone);
|
||||
|
||||
final double BT_LENGTH = 0.09;
|
||||
BodyTube bodyTube = new BodyTube(BT_LENGTH, SIDE_RADIUS, THICKNESS);
|
||||
bodyTube.setName("Side Booster Body Tubes");
|
||||
bodyTube.setMotorMount(true);
|
||||
sideBoosters.addChild(bodyTube);
|
||||
|
||||
MotorConfiguration motorConfig = new MotorConfiguration(bodyTube, selFCID);
|
||||
motorConfig.setMotor(TestRockets.generateMotor_A10_13mm());
|
||||
motorConfig.setEjectionDelay(0.0);
|
||||
motorConfig.setIgnitionEvent(IgnitionEvent.LAUNCH);
|
||||
bodyTube.setMotorConfig(motorConfig, selFCID);
|
||||
|
||||
StageSeparationConfiguration stageSepConfig = new StageSeparationConfiguration();
|
||||
stageSepConfig.setSeparationEvent(StageSeparationConfiguration.SeparationEvent.BURNOUT);
|
||||
sideBoosters.getSeparationConfigurations().set(selFCID, stageSepConfig);
|
||||
}
|
||||
|
||||
rocket.enableEvents();
|
||||
rocket.setSelectedConfiguration(selFCID);
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
// This is a simple four-fin rocket with large endplates on the
|
||||
// fins, for testing CG and CP calculations with fins on pods.
|
||||
// not a complete rocket (no motor mount nor recovery system)
|
||||
|
@ -68,8 +68,7 @@ public class FlightEventsTest extends BaseTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testMultiStage() throws SimulationException {
|
||||
final Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
TestRockets.addCoreFins(rocket);
|
||||
final Rocket rocket = TestRockets.makeMultiStageEventTestRocket();
|
||||
|
||||
final Simulation sim = new Simulation(rocket);
|
||||
sim.getOptions().setISAAtmosphere(true);
|
||||
@ -80,65 +79,85 @@ public class FlightEventsTest extends BaseTestCase {
|
||||
|
||||
sim.simulate();
|
||||
|
||||
/*
|
||||
for (int b = 0; b < sim.getSimulatedData().getBranchCount(); b++) {
|
||||
System.out.println("branch " + b);
|
||||
for (FlightEvent event : sim.getSimulatedData().getBranch(b).getEvents()) {
|
||||
System.out.println(" " + event);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Test branch count
|
||||
final int branchCount = sim.getSimulatedData().getBranchCount();
|
||||
assertEquals(" Multi-stage simulation invalid branch count ", 3, branchCount);
|
||||
|
||||
final AxialStage coreStage = rocket.getStage(1);
|
||||
final ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
|
||||
final InnerTube boosterMotorTubes = (InnerTube) boosterStage.getChild(1).getChild(0);
|
||||
final BodyTube coreBody = (BodyTube) coreStage.getChild(0);
|
||||
|
||||
final AxialStage sustainer = rocket.getStage(0);
|
||||
final BodyTube sustainerBody = (BodyTube) sustainer.getChild(1);
|
||||
final Parachute sustainerChute = (Parachute) sustainerBody.getChild(0);
|
||||
|
||||
final AxialStage centerBooster = rocket.getStage(1);
|
||||
final BodyTube centerBoosterBody = (BodyTube) centerBooster.getChild(0);
|
||||
|
||||
final ParallelStage sideBoosters = (ParallelStage) centerBoosterBody.getChild(1);
|
||||
final BodyTube sideBoosterBodies = (BodyTube) sideBoosters.getChild(1);
|
||||
|
||||
// events whose time is too variable to check are given a time of 1200
|
||||
for (int b = 0; b < 3; b++) {
|
||||
for (int b = 0; b < 2; b++) {
|
||||
FlightEvent[] expectedEvents;
|
||||
switch (b) {
|
||||
// Sustainer (payload fairing stage)
|
||||
// Sustainer
|
||||
case 0:
|
||||
expectedEvents = new FlightEvent[] {
|
||||
new FlightEvent(FlightEvent.Type.LAUNCH, 0.0, rocket),
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.0, boosterMotorTubes),
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.0, coreBody),
|
||||
new FlightEvent(FlightEvent.Type.LIFTOFF, 0.1275, null),
|
||||
new FlightEvent(FlightEvent.Type.LAUNCHROD, 0.130, null),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 2.0, boosterMotorTubes),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.0, boosterStage),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.0, boosterStage),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 2.0, coreBody),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.0, coreStage),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.0, coreStage),
|
||||
new FlightEvent(FlightEvent.Type.APOGEE, 2.12, rocket),
|
||||
new FlightEvent(FlightEvent.Type.TUMBLE, 2.202, null),
|
||||
new FlightEvent(FlightEvent.Type.GROUND_HIT, 14.12, null),
|
||||
new FlightEvent(FlightEvent.Type.SIMULATION_END, 14.12, null)
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.0, sideBoosterBodies),
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.01, centerBoosterBody),
|
||||
new FlightEvent(FlightEvent.Type.LIFTOFF, 0.8255, null),
|
||||
new FlightEvent(FlightEvent.Type.LAUNCHROD, 0.828, null),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 0.85, sideBoosterBodies),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 0.85, sideBoosters),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 0.85, sideBoosters),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 2.01, centerBoosterBody),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.01, centerBooster),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.01, centerBooster),
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 2.01, sustainerBody),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 4.01, sustainerBody),
|
||||
new FlightEvent(FlightEvent.Type.APOGEE, 8.5, rocket),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 9.01, sustainer),
|
||||
new FlightEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, 9.01, sustainerChute),
|
||||
new FlightEvent(FlightEvent.Type.GROUND_HIT, 83.27, null),
|
||||
new FlightEvent(FlightEvent.Type.SIMULATION_END, 83.27, null)
|
||||
};
|
||||
break;
|
||||
// Core stage
|
||||
|
||||
// Center Booster
|
||||
case 1:
|
||||
expectedEvents = new FlightEvent[] {
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.0, coreBody),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 2.0, coreBody),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.0, coreStage),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.0, coreStage),
|
||||
new FlightEvent(FlightEvent.Type.APOGEE, 2.1, rocket),
|
||||
new FlightEvent(FlightEvent.Type.TUMBLE, 2.15, null),
|
||||
new FlightEvent(FlightEvent.Type.GROUND_HIT, 7.26, null),
|
||||
new FlightEvent(FlightEvent.Type.SIMULATION_END, 7.26, null)
|
||||
};
|
||||
break;
|
||||
// Booster stage
|
||||
case 2:
|
||||
expectedEvents = new FlightEvent[] {
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.0, boosterMotorTubes),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 2.0, boosterMotorTubes),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.0, boosterStage),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.0, boosterStage),
|
||||
new FlightEvent(FlightEvent.Type.TUMBLE, 2.05, null),
|
||||
new FlightEvent(FlightEvent.Type.APOGEE, 2.06, rocket),
|
||||
new FlightEvent(FlightEvent.Type.GROUND_HIT, 12.0, null),
|
||||
new FlightEvent(FlightEvent.Type.SIMULATION_END, 12.0, null)
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.01, centerBoosterBody),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 2.01, centerBoosterBody),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 2.01, centerBooster),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 2.01, centerBooster),
|
||||
new FlightEvent(FlightEvent.Type.TUMBLE, 2.85, null),
|
||||
new FlightEvent(FlightEvent.Type.APOGEE, 1200, rocket),
|
||||
new FlightEvent(FlightEvent.Type.GROUND_HIT, 1200, null),
|
||||
new FlightEvent(FlightEvent.Type.SIMULATION_END, 1200, null)
|
||||
};
|
||||
break;
|
||||
|
||||
// Side Boosters
|
||||
case 2:
|
||||
expectedEvents = new FlightEvent[] {
|
||||
new FlightEvent(FlightEvent.Type.IGNITION, 0.0, sideBoosterBodies),
|
||||
new FlightEvent(FlightEvent.Type.BURNOUT, 0.85, sideBoosterBodies),
|
||||
new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, 0.85, sideBoosters),
|
||||
new FlightEvent(FlightEvent.Type.STAGE_SEPARATION, 0.85, sideBoosters),
|
||||
new FlightEvent(FlightEvent.Type.TUMBLE, 1.0, null),
|
||||
new FlightEvent(FlightEvent.Type.APOGEE, 1.01, rocket),
|
||||
new FlightEvent(FlightEvent.Type.GROUND_HIT, 1.21, null),
|
||||
new FlightEvent(FlightEvent.Type.SIMULATION_END, 1.21, null)
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("Invalid branch number " + b);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user