package net.sf.openrocket.optimization.rocketoptimization.domains;
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.masscalc.BasicMassCalculator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.optimization.rocketoptimization.SimulationDomain;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.unit.Value;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Pair;
/**
* A simulation domain that limits the required stability of the rocket.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class StabilityDomain implements SimulationDomain {
private final double minimum;
private final boolean minAbsolute;
private final double maximum;
private final boolean maxAbsolute;
/**
* Sole constructor.
*
* @param minimum minimum stability requirement (or <code>NaN</code> for no limit)
* @param minAbsolute <code>true</code> if minimum is an absolute SI measurement,
* <code>false</code> if it is relative to the rocket caliber
* @param maximum maximum stability requirement (or <code>NaN</code> for no limit)
* @param maxAbsolute <code>true</code> if maximum is an absolute SI measurement,
* <code>false</code> if it is relative to the rocket caliber
*/
public StabilityDomain(double minimum, boolean minAbsolute, double maximum, boolean maxAbsolute) {
super();
this.minimum = minimum;
this.minAbsolute = minAbsolute;
this.maximum = maximum;
this.maxAbsolute = maxAbsolute;
}
@Override
public Pair<Double, Value> getDistanceToDomain(Simulation simulation) {
Coordinate cp, cg;
double cpx, cgx;
double absolute;
double relative;
/*
* These are instantiated each time because this class must be thread-safe.
* Caching would in any case be inefficient since the rocket changes all the time.
*/
AerodynamicCalculator aerodynamicCalculator = new BarrowmanCalculator();
MassCalculator massCalculator = new BasicMassCalculator();
Configuration configuration = simulation.getConfiguration();
FlightConditions conditions = new FlightConditions(configuration);
conditions.setMach(Application.getPreferences().getDefaultMach());
conditions.setAOA(0);
conditions.setRollRate(0);
// TODO: HIGH: This re-calculates the worst theta value every time
cp = aerodynamicCalculator.getWorstCP(configuration, conditions, null);
cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS);
if (cp.weight > 0.000001)
cpx = cp.x;
else
cpx = Double.NaN;
if (cg.weight > 0.000001)
cgx = cg.x;
else
cgx = Double.NaN;
// Calculate the reference (absolute or relative)
absolute = cpx - cgx;
double diameter = 0;
for (RocketComponent c : configuration) {
if (c instanceof SymmetricComponent) {
double d1 = ((SymmetricComponent) c).getForeRadius() * 2;
double d2 = ((SymmetricComponent) c).getAftRadius() * 2;
diameter = MathUtil.max(diameter, d1, d2);
}
}
relative = absolute / diameter;
Value desc;
if (minAbsolute && maxAbsolute) {
desc = new Value(absolute, UnitGroup.UNITS_LENGTH);
} else {
desc = new Value(relative, UnitGroup.UNITS_STABILITY_CALIBERS);
}
double ref;
if (minAbsolute) {
ref = minimum - absolute;
if (ref > 0) {
return new Pair<Double, Value>(ref, desc);
}
} else {
ref = minimum - relative;
if (ref > 0) {
return new Pair<Double, Value>(ref, desc);
}
}
if (maxAbsolute) {
ref = absolute - maximum;
if (ref > 0) {
return new Pair<Double, Value>(ref, desc);
}
} else {
ref = relative - maximum;
if (ref > 0) {
return new Pair<Double, Value>(ref, desc);
}
}
return new Pair<Double, Value>(0.0, desc);
}
}