Move test for stall angle to a method in BarrowmanCalculator, and call it in BasicEventSimulationEngine; this replaces the separate tests in BarrowmanCalculator (which may set a warning) and in BasicEventSimulationEngine (which may transition to tumbling) -- note the angles were not synchronized between the two!

BasicEventSimulationEngine is now responsible for both setting the warning and the transition to tumbling (if appropriate).
This commit is contained in:
JoePfeiffer 2024-10-15 10:54:27 -06:00
parent 90b2ece4fa
commit ccb3937551
7 changed files with 122 additions and 33 deletions

View File

@ -32,6 +32,18 @@ public abstract class AbstractAerodynamicCalculator implements AerodynamicCalcul
private ModID rocketAeroModID = new ModID();
private ModID rocketTreeModID = new ModID();
/**
* Determine whether calculations are suspect because we are stalling
*
* @return whether we are stalling, and the margin
* between our AOA and a stall
* If the return is positive we aren't;
* If it's negative we are.
*
*/
@Override
public abstract double getStallMargin();
//////////////// Aerodynamic calculators ////////////////
@Override

View File

@ -15,6 +15,17 @@ import info.openrocket.core.util.Monitorable;
*/
public interface AerodynamicCalculator extends Monitorable {
/**
* Determine whether calculations are suspect because we are stalling
*
* @return whether we are stalling, and the margin
* between our AOA and a stall
* If the return is positive we aren't;
* If it's negative we are.
*
*/
public double getStallMargin();
/**
* Calculate the CP of the specified configuration.
*

View File

@ -56,6 +56,9 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
private double cacheDiameter = -1;
private double cacheLength = -1;
private final double stallAngle = 17.5 * Math.PI / 180;
private double stallMargin;
public BarrowmanCalculator() {
}
@ -66,6 +69,19 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
return new BarrowmanCalculator();
}
/**
* Determine whether calculations are suspect because we are stalling
*
* @return whether we are stalling, and the margin
* between our AOA and a stall
* If the return is positive we aren't;
* If it's negative we are.
*
*/
@Override
public double getStallMargin() {
return stallMargin;
}
/**
* Calculate the CP according to the extended Barrowman method.
@ -215,6 +231,9 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
total.setCm(total.getCm() - total.getPitchDampingMoment());
total.setCyaw(total.getCyaw() - total.getYawDampingMoment());
// How far are we from stalling?
stallMargin = stallAngle - conditions.getAOA();
return total;
}
@ -258,9 +277,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
if (warnings == null)
warnings = ignoreWarningSet;
if (conditions.getAOA() > 17.5 * Math.PI / 180)
warnings.add(new Warning.LargeAOA(conditions.getAOA()));
if (calcMap == null)
buildCalcMap(configuration);

View File

@ -246,9 +246,6 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
public Coordinate launchRodDirection = null;
public double maxZvelocity = Double.NaN;
public double startWarningTime = Double.NaN;
// set by calculateFlightConditions and calculateAcceleration:
public AerodynamicForces forces;
public Coordinate windVelocity = new Coordinate(Double.NaN, Double.NaN, Double.NaN);

View File

@ -42,10 +42,6 @@ public class BasicEventSimulationEngine implements SimulationEngine {
private final SimulationStepper tumbleStepper = new BasicTumbleStepper();
private final SimulationStepper groundStepper = new GroundStepper();
// Constant holding 20 degrees in radians. This is the AOA condition
// necessary to transition to tumbling.
private final static double AOA_TUMBLE_CONDITION = Math.PI / 9.0;
// The thrust must be below this value for the transition to tumbling.
// TODO HIGH: this is an arbitrary value
private final static double THRUST_TUMBLE_CONDITION = 0.01;
@ -265,15 +261,29 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Conditions for transition are:
// is not already tumbling
// and not stable (cg > cp)
// and aoa > AOA_TUMBLE_CONDITION threshold
// and stallMargin() < 0
if (!currentStatus.isTumbling()) {
final double cp = currentStatus.getFlightDataBranch().getLast(FlightDataType.TYPE_CP_LOCATION);
final double cg = currentStatus.getFlightDataBranch().getLast(FlightDataType.TYPE_CG_LOCATION);
final double aoa = currentStatus.getFlightDataBranch().getLast(FlightDataType.TYPE_AOA);
final double margin =
currentStatus.getSimulationConditions().getAerodynamicCalculator().getStallMargin();
if (cg > cp && aoa > AOA_TUMBLE_CONDITION) {
// large AOA -- stalling.
if (margin < 0) {
// If we're stable, put a warning about large AOA
// note -- if cp is NaN (which it is while on the rod) cg > cp is false
if (cg > cp) {
// Not stable, so transition to tumbling
currentStatus.addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, currentStatus.getSimulationTime()));
} else {
// Stable, so warning about AOA
if (currentStatus.recordWarnings()) {
currentStatus.addEvent(new FlightEvent(FlightEvent.Type.SIM_WARN, currentStatus.getSimulationTime(), null,
new Warning.LargeAOA(aoa)));
}
}
}
}

View File

@ -401,22 +401,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
* launch rod or 0.25 seconds after departure, and when the velocity has dropped
* below 20% of the max. velocity.
*/
WarningSet warnings = new WarningSet();
store.maxZvelocity = MathUtil.max(store.maxZvelocity, status.getRocketVelocity().z);
if (!status.isLaunchRodCleared()) {
warnings = null;
} else {
if (status.getRocketVelocity().z < 0.2 * store.maxZvelocity) {
warnings = null;
}
if (Double.isNaN(store.startWarningTime)) {
store.startWarningTime = status.getSimulationTime() + 0.25;
}
}
if (!(status.getSimulationTime() > store.startWarningTime))
warnings = null;
WarningSet warnings = status.recordWarnings() ? new WarningSet() : null;
// Calculate aerodynamic forces
store.forces = status.getSimulationConditions().getAerodynamicCalculator()

View File

@ -38,6 +38,13 @@ import org.slf4j.LoggerFactory;
public class SimulationStatus implements Cloneable, Monitorable {
// time after leaving launch rod before recording flight event warnings
private final double WARNINGS_WAIT = 0.25;
// when our z velocity decreases to this proportion of max z velocity, stop recording
// most flight event warnings
private final double WARNINGS_VEL = 0.2;
private static final Logger log = LoggerFactory.getLogger(BasicEventSimulationEngine.class);
private SimulationConditions simulationConditions;
@ -54,6 +61,9 @@ public class SimulationStatus implements Cloneable, Monitorable {
private Quaternion orientation;
private Coordinate rotationVelocity;
private double maxZVelocity = Double.NEGATIVE_INFINITY;
private double startWarningsTime = 1200;
private double effectiveLaunchRodLength;
// Set of all motors
@ -189,6 +199,8 @@ public class SimulationStatus implements Cloneable, Monitorable {
this.apogeeReached = orig.apogeeReached;
this.tumbling = orig.tumbling;
this.landed = orig.landed;
this.maxZVelocity = orig.maxZVelocity;
this.startWarningsTime = orig.startWarningsTime;
this.configuration.copyStages(orig.configuration);
@ -357,6 +369,9 @@ public class SimulationStatus implements Cloneable, Monitorable {
public void setLaunchRodCleared(boolean launchRod) {
this.launchRodCleared = launchRod;
if (launchRod) {
startWarningsTime = getSimulationTime() + WARNINGS_WAIT;
}
modID = new ModID();
}
@ -550,6 +565,8 @@ public class SimulationStatus implements Cloneable, Monitorable {
flightDataBranch.setValue(FlightDataType.TYPE_VELOCITY_XY,
MathUtil.hypot(getRocketVelocity().x, getRocketVelocity().y));
flightDataBranch.setValue(FlightDataType.TYPE_VELOCITY_Z, getRocketVelocity().z);
setMaxZVelocity(Math.max(getRocketVelocity().z, getMaxZVelocity()));
flightDataBranch.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, getRocketVelocity().length());
Coordinate c = getRocketOrientationQuaternion().rotateZ();
@ -563,6 +580,46 @@ public class SimulationStatus implements Cloneable, Monitorable {
(System.nanoTime() - getSimulationStartWallTime()) / 1000000000.0);
}
/**
* Get max Z velocity so far in flight
* @return max Z velocity so far
*/
public double getMaxZVelocity() {
return maxZVelocity;
}
/**
* Set max Z velocity so far
* @param zVel current z velocity
*/
private void setMaxZVelocity(double zVel) {
if (zVel > maxZVelocity) {
maxZVelocity = zVel;
modID = new ModID();
}
}
/**
* Determine whether (most) flight event warnings are currently being saved.
* Warnings are not saved until 0.25 seconds after leaving the rail, and again
* after Z velocity is reduced to 20% of the max.
*/
boolean recordWarnings() {
if (!launchRodCleared) {
return false;
}
if (getSimulationTime() < startWarningsTime) {
return false;
}
if (getRocketVelocity().z < getMaxZVelocity() * 0.2) {
return false;
}
return true;
}
/**
* Add a flight event to the event queue unless a listener aborts adding it.
*
@ -570,6 +627,7 @@ public class SimulationStatus implements Cloneable, Monitorable {
*/
public void addEvent(FlightEvent event) throws SimulationException {
if (SimulationListenerHelper.fireAddFlightEvent(this, event)) {
if (event.getType() != FlightEvent.Type.ALTITUDE) {
log.trace("Adding event to queue: " + event);
}