package net.sf.openrocket.models.atmosphere; import static net.sf.openrocket.models.atmosphere.AtmosphericConditions.R; import net.sf.openrocket.util.MathUtil; /** * An atmospheric temperature/pressure model based on the International Standard Atmosphere * (ISA). The no-argument constructor creates an {@link AtmosphericModel} that corresponds * to the ISA model. It is extended by the other constructors to allow defining a custom * first layer. The base temperature and pressure are as given, and all other values * are calculated based on these. * <p> * TODO: LOW: Values at altitudes over 32km differ from standard results by ~5%. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public class ExtendedISAModel extends InterpolatingAtmosphericModel { public static final double STANDARD_TEMPERATURE = 288.15; public static final double STANDARD_PRESSURE = 101325; private static final double G = 9.80665; private final double[] layer = { 0, 11000, 20000, 32000, 47000, 51000, 71000, 84852 }; private final double[] baseTemperature = { 288.15, 216.65, 216.65, 228.65, 270.65, 270.65, 214.65, 186.95 }; private final double[] basePressure = new double[layer.length]; /** * Construct the standard ISA model. */ public ExtendedISAModel() { this(STANDARD_TEMPERATURE, STANDARD_PRESSURE); } /** * Construct an extended model with the given temperature and pressure at MSL. * * @param temperature the temperature at MSL. * @param pressure the pressure at MSL. */ public ExtendedISAModel(double temperature, double pressure) { this(0, temperature, pressure); } /** * Construct an extended model with the given temperature and pressure at the * specified altitude. Conditions below the given altitude cannot be calculated, * and the values at the specified altitude will be returned instead. The altitude * must be lower than the altitude of the next ISA standard layer (below 11km). * * @param altitude the altitude of the measurements. * @param temperature the temperature. * @param pressure the pressure. * @throws IllegalArgumentException if the altitude exceeds the second layer boundary * of the ISA model (over 11km). */ public ExtendedISAModel(double altitude, double temperature, double pressure) { if (altitude >= layer[1]) { throw new IllegalArgumentException("Too high first altitude: " + altitude); } layer[0] = altitude; baseTemperature[0] = temperature; basePressure[0] = pressure; for (int i = 1; i < basePressure.length; i++) { basePressure[i] = getExactConditions(layer[i] - 1).getPressure(); } } @Override protected AtmosphericConditions getExactConditions(double altitude) { altitude = MathUtil.clamp(altitude, layer[0], layer[layer.length - 1]); int n; for (n = 0; n < layer.length - 1; n++) { if (layer[n + 1] > altitude) break; } double rate = (baseTemperature[n + 1] - baseTemperature[n]) / (layer[n + 1] - layer[n]); double t = baseTemperature[n] + (altitude - layer[n]) * rate; double p; if (Math.abs(rate) > 0.001) { p = basePressure[n] * Math.pow(1 + (altitude - layer[n]) * rate / baseTemperature[n], -G / (rate * R)); } else { p = basePressure[n] * Math.exp(-(altitude - layer[n]) * G / (R * baseTemperature[n])); } return new AtmosphericConditions(t, p); } @Override protected double getMaxAltitude() { return layer[layer.length - 1]; } public static void main(String foo[]) { ExtendedISAModel model1 = new ExtendedISAModel(); ExtendedISAModel model2 = new ExtendedISAModel(278.15, 100000); for (double alt = 0; alt < 80000; alt += 500) { AtmosphericConditions cond1 = model1.getConditions(alt); AtmosphericConditions cond2 = model2.getConditions(alt); AtmosphericConditions diff = new AtmosphericConditions(); diff.setPressure((cond2.getPressure() - cond1.getPressure()) / cond1.getPressure() * 100); diff.setTemperature((cond2.getTemperature() - cond1.getTemperature()) / cond1.getTemperature() * 100); //System.out.println("alt=" + alt + ": std:" + cond1 + " mod:" + cond2 + " diff:" + diff); } } @Override public int getModID() { return 0; } }