Merge pull request #2457 from JoePfeiffer/allow-sustainer-tumble

Allow sustainer tumble before apogee
This commit is contained in:
Sibo Van Gool 2024-02-19 17:11:13 +01:00 committed by GitHub
commit 7c246e7144
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 223 additions and 52 deletions

View File

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

View File

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

View File

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