package net.sf.openrocket.unit; import static net.sf.openrocket.util.Chars.CUBED; import static net.sf.openrocket.util.Chars.DEGREE; import static net.sf.openrocket.util.Chars.DOT; import static net.sf.openrocket.util.Chars.MICRO; import static net.sf.openrocket.util.Chars.PERMILLE; import static net.sf.openrocket.util.Chars.SQUARED; import static net.sf.openrocket.util.Chars.ZWSP; import static net.sf.openrocket.util.MathUtil.pow2; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.Rocket; /** * A group of units (eg. length, mass etc.). Contains a list of different units of a same * quantity. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public class UnitGroup { public static final UnitGroup UNITS_NONE; public static final UnitGroup UNITS_MOTOR_DIMENSIONS; public static final UnitGroup UNITS_LENGTH; public static final UnitGroup UNITS_ALL_LENGTHS; public static final UnitGroup UNITS_DISTANCE; public static final UnitGroup UNITS_AREA; public static final UnitGroup UNITS_STABILITY; /** * This unit group contains only the caliber unit that never scales the originating "SI" value. * It can be used in cases where the originating value is already in calibers to obtains the correct unit. */ public static final UnitGroup UNITS_STABILITY_CALIBERS; public static final UnitGroup UNITS_VELOCITY; public static final UnitGroup UNITS_WINDSPEED; public static final UnitGroup UNITS_ACCELERATION; public static final UnitGroup UNITS_MASS; public static final UnitGroup UNITS_INERTIA; public static final UnitGroup UNITS_ANGLE; public static final UnitGroup UNITS_DENSITY_BULK; public static final UnitGroup UNITS_DENSITY_SURFACE; public static final UnitGroup UNITS_DENSITY_LINE; public static final UnitGroup UNITS_FORCE; public static final UnitGroup UNITS_IMPULSE; /** Time in the order of less than a second (time step etc). */ public static final UnitGroup UNITS_TIME_STEP; /** Time in the order of seconds (motor delay etc). */ public static final UnitGroup UNITS_SHORT_TIME; /** Time in the order of the flight time of a rocket. */ public static final UnitGroup UNITS_FLIGHT_TIME; public static final UnitGroup UNITS_ROLL; public static final UnitGroup UNITS_TEMPERATURE; public static final UnitGroup UNITS_PRESSURE; public static final UnitGroup UNITS_RELATIVE; public static final UnitGroup UNITS_ROUGHNESS; public static final UnitGroup UNITS_COEFFICIENT; public static final UnitGroup UNITS_FREQUENCY; public static final UnitGroup UNITS_ENERGY; public static final UnitGroup UNITS_POWER; public static final UnitGroup UNITS_MOMENTUM; public static final UnitGroup UNITS_VOLTAGE; public static final UnitGroup UNITS_CURRENT; public static final Map<String, UnitGroup> UNITS; // keys such as "LENGTH", "VELOCITY" public static final Map<String, UnitGroup> SIUNITS; // keys such a "m", "m/s" /* * Note: Units may not use HTML tags. * * The scaling value "X" is obtained by "one of this unit is X of SI units" * Type into Google for example: "1 in^2 in m^2" */ static { UNITS_NONE = new UnitGroup(); UNITS_NONE.addUnit(Unit.NOUNIT); UNITS_NONE.setDefaultUnit(0); UNITS_ENERGY = new UnitGroup(); UNITS_ENERGY.addUnit(new GeneralUnit(1, "J")); UNITS_ENERGY.addUnit(new GeneralUnit(1e-7, "erg")); UNITS_ENERGY.addUnit(new GeneralUnit(1.055, "BTU")); UNITS_ENERGY.addUnit(new GeneralUnit(4.184, "cal")); UNITS_ENERGY.addUnit(new GeneralUnit(1.3558179483314, "ft" + DOT + "lbf")); UNITS_ENERGY.setDefaultUnit(0); UNITS_POWER = new UnitGroup(); UNITS_POWER.addUnit(new GeneralUnit(1e-3, "mW")); UNITS_POWER.addUnit(new GeneralUnit(1, "W")); UNITS_POWER.addUnit(new GeneralUnit(1e3, "kW")); UNITS_POWER.addUnit(new GeneralUnit(1e-7, "ergs")); UNITS_POWER.addUnit(new GeneralUnit(745.699872, "hp")); UNITS_POWER.setDefaultUnit(1); UNITS_MOMENTUM = new UnitGroup(); UNITS_MOMENTUM.addUnit(new GeneralUnit(1, "kg" + DOT + "m/s")); UNITS_MOMENTUM.setDefaultUnit(0); UNITS_VOLTAGE = new UnitGroup(); UNITS_VOLTAGE.addUnit(new GeneralUnit(1e-3, "mV")); UNITS_VOLTAGE.addUnit(new GeneralUnit(1, "V")); UNITS_VOLTAGE.setDefaultUnit(1); UNITS_CURRENT = new UnitGroup(); UNITS_CURRENT.addUnit(new GeneralUnit(1e-3, "mA")); UNITS_CURRENT.addUnit(new GeneralUnit(1, "A")); UNITS_CURRENT.setDefaultUnit(1); UNITS_LENGTH = new UnitGroup(); UNITS_LENGTH.addUnit(new GeneralUnit(0.001, "mm")); UNITS_LENGTH.addUnit(new GeneralUnit(0.01, "cm")); UNITS_LENGTH.addUnit(new GeneralUnit(1, "m")); UNITS_LENGTH.addUnit(new InchUnit(0.0254, "in")); UNITS_LENGTH.addUnit(new FractionalUnit(0.0254, "in/64", "in", 64, 1d / 16d, 0.5d / 64d)); UNITS_LENGTH.addUnit(new GeneralUnit(0.3048, "ft")); UNITS_LENGTH.setDefaultUnit(1); UNITS_MOTOR_DIMENSIONS = new UnitGroup(); UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001, "mm")); UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01, "cm")); UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(1, "m")); UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254, "in")); UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0); UNITS_DISTANCE = new UnitGroup(); UNITS_DISTANCE.addUnit(new GeneralUnit(1, "m")); UNITS_DISTANCE.addUnit(new GeneralUnit(1000, "km")); UNITS_DISTANCE.addUnit(new GeneralUnit(0.3048, "ft")); UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144, "yd")); UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344, "mi")); UNITS_DISTANCE.addUnit(new GeneralUnit(1852, "nmi")); UNITS_DISTANCE.setDefaultUnit(0); UNITS_ALL_LENGTHS = new UnitGroup(); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.001, "mm")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.01, "cm")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1, "m")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1000, "km")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.0254, "in")); UNITS_ALL_LENGTHS.addUnit(new FractionalUnit(0.0254, "in/64", "in", 64, 1d / 16d, 0.5d / 64d)); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.3048, "ft")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.9144, "yd")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1609.344, "mi")); UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1852, "nmi")); UNITS_ALL_LENGTHS.setDefaultUnit(2); UNITS_AREA = new UnitGroup(); UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001), "mm" + SQUARED)); UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01), "cm" + SQUARED)); UNITS_AREA.addUnit(new GeneralUnit(1, "m" + SQUARED)); UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254), "in" + SQUARED)); UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048), "ft" + SQUARED)); UNITS_AREA.setDefaultUnit(1); UNITS_STABILITY = new UnitGroup(); UNITS_STABILITY.addUnit(new GeneralUnit(0.001, "mm")); UNITS_STABILITY.addUnit(new GeneralUnit(0.01, "cm")); UNITS_STABILITY.addUnit(new GeneralUnit(1, "m")); UNITS_STABILITY.addUnit(new GeneralUnit(0.0254, "in")); UNITS_STABILITY.addUnit(new CaliberUnit((Rocket) null)); UNITS_STABILITY.setDefaultUnit(4); UNITS_STABILITY_CALIBERS = new UnitGroup(); UNITS_STABILITY_CALIBERS.addUnit(new GeneralUnit(1, "cal")); UNITS_STABILITY_CALIBERS.setDefaultUnit(0); UNITS_VELOCITY = new UnitGroup(); UNITS_VELOCITY.addUnit(new GeneralUnit(1, "m/s")); UNITS_VELOCITY.addUnit(new GeneralUnit(1 / 3.6, "km/h")); UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s")); UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph")); UNITS_VELOCITY.addUnit(new GeneralUnit(0.51444445, "kt")); UNITS_VELOCITY.setDefaultUnit(0); UNITS_WINDSPEED = new UnitGroup(); UNITS_WINDSPEED.addUnit(new GeneralUnit(1, "m/s")); UNITS_WINDSPEED.addUnit(new GeneralUnit(1 / 3.6, "km/h")); UNITS_WINDSPEED.addUnit(new GeneralUnit(0.3048, "ft/s")); UNITS_WINDSPEED.addUnit(new GeneralUnit(0.44704, "mph")); UNITS_WINDSPEED.addUnit(new GeneralUnit(0.51444445, "kt")); UNITS_WINDSPEED.setDefaultUnit(0); UNITS_ACCELERATION = new UnitGroup(); UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED)); UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED)); UNITS_ACCELERATION.addUnit(new GeneralUnit(9.80665, "G")); UNITS_ACCELERATION.setDefaultUnit(0); UNITS_MASS = new UnitGroup(); UNITS_MASS.addUnit(new GeneralUnit(0.001, "g")); UNITS_MASS.addUnit(new GeneralUnit(1, "kg")); UNITS_MASS.addUnit(new GeneralUnit(0.0283495231, "oz")); UNITS_MASS.addUnit(new GeneralUnit(0.45359237, "lb")); UNITS_MASS.setDefaultUnit(0); UNITS_INERTIA = new UnitGroup(); UNITS_INERTIA.addUnit(new GeneralUnit(0.0001, "kg" + DOT + "cm" + SQUARED)); UNITS_INERTIA.addUnit(new GeneralUnit(1, "kg" + DOT + "m" + SQUARED)); UNITS_INERTIA.addUnit(new GeneralUnit(1.82899783e-5, "oz" + DOT + "in" + SQUARED)); UNITS_INERTIA.addUnit(new GeneralUnit(0.000292639653, "lb" + DOT + "in" + SQUARED)); UNITS_INERTIA.addUnit(new GeneralUnit(0.0421401101, "lb" + DOT + "ft" + SQUARED)); UNITS_INERTIA.addUnit(new GeneralUnit(1.35581795, "lbf" + DOT + "ft" + DOT + "s" + SQUARED)); UNITS_INERTIA.setDefaultUnit(1); UNITS_ANGLE = new UnitGroup(); UNITS_ANGLE.addUnit(new DegreeUnit()); UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad", 0.01)); UNITS_ANGLE.addUnit(new GeneralUnit(1.0 / 3437.74677078, "arcmin")); UNITS_ANGLE.setDefaultUnit(0); UNITS_DENSITY_BULK = new UnitGroup(); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000, "g/cm" + CUBED)); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000, "kg/dm" + CUBED)); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1, "kg/m" + CUBED)); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404, "oz/in" + CUBED)); UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634, "lb/ft" + CUBED)); UNITS_DENSITY_BULK.setDefaultUnit(0); UNITS_DENSITY_SURFACE = new UnitGroup(); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10, "g/cm" + SQUARED)); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001, "g/m" + SQUARED)); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1, "kg/m" + SQUARED)); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487, "oz/in" + SQUARED)); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727, "oz/ft" + SQUARED)); UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764, "lb/ft" + SQUARED)); UNITS_DENSITY_SURFACE.setDefaultUnit(1); UNITS_DENSITY_LINE = new UnitGroup(); UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001, "g/m")); UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1, "kg/m")); UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465, "oz/ft")); UNITS_DENSITY_LINE.setDefaultUnit(0); UNITS_FORCE = new UnitGroup(); UNITS_FORCE.addUnit(new GeneralUnit(1, "N")); UNITS_FORCE.addUnit(new GeneralUnit(4.44822162, "lbf")); UNITS_FORCE.addUnit(new GeneralUnit(9.80665, "kgf")); UNITS_FORCE.setDefaultUnit(0); UNITS_IMPULSE = new UnitGroup(); UNITS_IMPULSE.addUnit(new GeneralUnit(1, "Ns")); UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf" + DOT + "s")); UNITS_IMPULSE.setDefaultUnit(0); UNITS_TIME_STEP = new UnitGroup(); UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001)); UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("s", 0.01)); UNITS_TIME_STEP.setDefaultUnit(1); UNITS_SHORT_TIME = new UnitGroup(); UNITS_SHORT_TIME.addUnit(new GeneralUnit(1, "s")); UNITS_SHORT_TIME.setDefaultUnit(0); UNITS_FLIGHT_TIME = new UnitGroup(); UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1, "s")); UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60, "min")); UNITS_FLIGHT_TIME.setDefaultUnit(0); UNITS_ROLL = new UnitGroup(); UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s")); UNITS_ROLL.addUnit(new GeneralUnit(Math.PI / 180, DEGREE + "/s")); UNITS_ROLL.addUnit(new GeneralUnit(2 * Math.PI, "r/s")); UNITS_ROLL.addUnit(new GeneralUnit(2 * Math.PI / 60, "rpm")); UNITS_ROLL.setDefaultUnit(1); UNITS_TEMPERATURE = new UnitGroup(); UNITS_TEMPERATURE.addUnit(new FixedPrecisionUnit("K", 1)); UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, DEGREE + "C")); UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0 / 9.0, 459.67, DEGREE + "F")); UNITS_TEMPERATURE.setDefaultUnit(1); UNITS_PRESSURE = new UnitGroup(); UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("mbar", 1, 1.0e2)); UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("bar", 0.001, 1.0e5)); UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("atm", 0.001, 1.01325e5)); UNITS_PRESSURE.addUnit(new GeneralUnit(101325.0 / 760.0, "mmHg")); UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg")); UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi")); UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa")); UNITS_PRESSURE.setDefaultUnit(0); UNITS_RELATIVE = new UnitGroup(); UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0)); UNITS_RELATIVE.addUnit(new GeneralUnit(0.01, "%")); UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001)); UNITS_RELATIVE.setDefaultUnit(1); UNITS_ROUGHNESS = new UnitGroup(); UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO + "m")); UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil")); UNITS_ROUGHNESS.addUnit(new GeneralUnit(1, "m")); UNITS_ROUGHNESS.setDefaultUnit(0); UNITS_COEFFICIENT = new UnitGroup(); UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01)); // zero-width space UNITS_COEFFICIENT.setDefaultUnit(0); // This is not used by OpenRocket, and not extensively tested: UNITS_FREQUENCY = new UnitGroup(); UNITS_FREQUENCY.addUnit(new FrequencyUnit(.001, "mHz")); UNITS_FREQUENCY.addUnit(new FrequencyUnit(1, "Hz")); UNITS_FREQUENCY.addUnit(new FrequencyUnit(1000, "kHz")); UNITS_FREQUENCY.setDefaultUnit(1); HashMap<String, UnitGroup> map = new HashMap<String, UnitGroup>(); map.put("NONE", UNITS_NONE); map.put("LENGTH", UNITS_LENGTH); map.put("ALL_LENGTHS", UNITS_ALL_LENGTHS); map.put("MOTOR_DIMENSIONS", UNITS_MOTOR_DIMENSIONS); map.put("DISTANCE", UNITS_DISTANCE); map.put("VELOCITY", UNITS_VELOCITY); map.put("ACCELERATION", UNITS_ACCELERATION); map.put("AREA", UNITS_AREA); map.put("STABILITY", UNITS_STABILITY); map.put("MASS", UNITS_MASS); map.put("INERTIA", UNITS_INERTIA); map.put("ANGLE", UNITS_ANGLE); map.put("DENSITY_BULK", UNITS_DENSITY_BULK); map.put("DENSITY_SURFACE", UNITS_DENSITY_SURFACE); map.put("DENSITY_LINE", UNITS_DENSITY_LINE); map.put("FORCE", UNITS_FORCE); map.put("IMPULSE", UNITS_IMPULSE); map.put("TIME_STEP", UNITS_TIME_STEP); map.put("SHORT_TIME", UNITS_SHORT_TIME); map.put("FLIGHT_TIME", UNITS_FLIGHT_TIME); map.put("ROLL", UNITS_ROLL); map.put("TEMPERATURE", UNITS_TEMPERATURE); map.put("PRESSURE", UNITS_PRESSURE); map.put("RELATIVE", UNITS_RELATIVE); map.put("ROUGHNESS", UNITS_ROUGHNESS); map.put("COEFFICIENT", UNITS_COEFFICIENT); map.put("VOLTAGE", UNITS_VOLTAGE); map.put("CURRENT", UNITS_CURRENT); map.put("ENERGY", UNITS_ENERGY); map.put("POWER", UNITS_POWER); map.put("MOMENTUM", UNITS_MOMENTUM); map.put("FREQUENCY", UNITS_FREQUENCY); map.put("WINDSPEED", UNITS_WINDSPEED); UNITS = Collections.unmodifiableMap(map); HashMap<String, UnitGroup> simap = new HashMap<String, UnitGroup>(); simap.put("m", UNITS_ALL_LENGTHS); simap.put("m^2", UNITS_AREA); simap.put("m/s", UNITS_VELOCITY); simap.put("m/s^2", UNITS_ACCELERATION); simap.put("kg", UNITS_MASS); simap.put("kg m^2", UNITS_INERTIA); simap.put("kg/m^3", UNITS_DENSITY_BULK); simap.put("N", UNITS_FORCE); simap.put("Ns", UNITS_IMPULSE); simap.put("s", UNITS_FLIGHT_TIME); simap.put("Pa", UNITS_PRESSURE); simap.put("V", UNITS_VOLTAGE); simap.put("A", UNITS_CURRENT); simap.put("J", UNITS_ENERGY); simap.put("W", UNITS_POWER); simap.put("kg m/s", UNITS_MOMENTUM); simap.put("Hz", UNITS_FREQUENCY); simap.put("K", UNITS_TEMPERATURE); SIUNITS = Collections.unmodifiableMap(simap); } public static void setDefaultMetricUnits() { UNITS_LENGTH.setDefaultUnit("cm"); UNITS_MOTOR_DIMENSIONS.setDefaultUnit("mm"); UNITS_DISTANCE.setDefaultUnit("m"); UNITS_AREA.setDefaultUnit("cm" + SQUARED); UNITS_STABILITY.setDefaultUnit("cal"); UNITS_VELOCITY.setDefaultUnit("m/s"); UNITS_ACCELERATION.setDefaultUnit("m/s" + SQUARED); UNITS_MASS.setDefaultUnit("g"); UNITS_INERTIA.setDefaultUnit("kg" + DOT + "m" + SQUARED); UNITS_ANGLE.setDefaultUnit("" + DEGREE); UNITS_DENSITY_BULK.setDefaultUnit("g/cm" + CUBED); UNITS_DENSITY_SURFACE.setDefaultUnit("g/m" + SQUARED); UNITS_DENSITY_LINE.setDefaultUnit("g/m"); UNITS_FORCE.setDefaultUnit("N"); UNITS_IMPULSE.setDefaultUnit("Ns"); UNITS_TIME_STEP.setDefaultUnit("s"); UNITS_FLIGHT_TIME.setDefaultUnit("s"); UNITS_ROLL.setDefaultUnit("r/s"); UNITS_TEMPERATURE.setDefaultUnit(DEGREE + "C"); UNITS_WINDSPEED.setDefaultUnit("m/s"); UNITS_PRESSURE.setDefaultUnit("mbar"); UNITS_RELATIVE.setDefaultUnit("%"); UNITS_ROUGHNESS.setDefaultUnit(MICRO + "m"); } public static void setDefaultImperialUnits() { UNITS_LENGTH.setDefaultUnit("in"); UNITS_MOTOR_DIMENSIONS.setDefaultUnit("in"); UNITS_DISTANCE.setDefaultUnit("ft"); UNITS_AREA.setDefaultUnit("in" + SQUARED); UNITS_STABILITY.setDefaultUnit("cal"); UNITS_VELOCITY.setDefaultUnit("ft/s"); UNITS_ACCELERATION.setDefaultUnit("ft/s" + SQUARED); UNITS_MASS.setDefaultUnit("oz"); UNITS_INERTIA.setDefaultUnit("lb" + DOT + "ft" + SQUARED); UNITS_ANGLE.setDefaultUnit("" + DEGREE); UNITS_DENSITY_BULK.setDefaultUnit("oz/in" + CUBED); UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft" + SQUARED); UNITS_DENSITY_LINE.setDefaultUnit("oz/ft"); UNITS_FORCE.setDefaultUnit("N"); UNITS_IMPULSE.setDefaultUnit("Ns"); UNITS_TIME_STEP.setDefaultUnit("s"); UNITS_FLIGHT_TIME.setDefaultUnit("s"); UNITS_ROLL.setDefaultUnit("r/s"); UNITS_TEMPERATURE.setDefaultUnit(DEGREE + "F"); UNITS_WINDSPEED.setDefaultUnit("mph"); UNITS_PRESSURE.setDefaultUnit("mbar"); UNITS_RELATIVE.setDefaultUnit("%"); UNITS_ROUGHNESS.setDefaultUnit("mil"); } /** * Return a UnitGroup for stability units based on the rocket. * * @param rocket the rocket from which to calculate the caliber * @return the unit group */ public static UnitGroup stabilityUnits(Rocket rocket) { return new StabilityUnitGroup(rocket); } /** * Return a UnitGroup for stability units based on the rocket configuration. * * @param config the rocket configuration from which to calculate the caliber * @return the unit group */ public static UnitGroup stabilityUnits(Configuration config) { return new StabilityUnitGroup(config); } /** * Return a UnitGroup for stability units based on a constant caliber. * * @param reference the constant reference length * @return the unit group */ public static UnitGroup stabilityUnits(double reference) { return new StabilityUnitGroup(reference); } ////////////////////////////////////////////////////// protected ArrayList<Unit> units = new ArrayList<Unit>(); protected int defaultUnit = 0; public int getUnitCount() { return units.size(); } public Unit getDefaultUnit() { return units.get(defaultUnit); } public int getDefaultUnitIndex() { return defaultUnit; } public void setDefaultUnit(int n) { if (n < 0 || n >= units.size()) { throw new IllegalArgumentException("index out of range: " + n); } defaultUnit = n; } public Unit getSIUnit() { for (Unit u : units) { if (u.multiplier == 1) { return u; } } return UNITS_NONE.getDefaultUnit(); } /** * Find a unit by approximate unit name. Only letters and (ordinary) numbers are * considered in the matching. This method is mainly means for testing, allowing * a simple means to obtain a particular unit. * * @param str the unit name. * @return the corresponding unit, or <code>null</code> if not found. */ public Unit findApproximate(String str) { str = str.replaceAll("\\W", "").trim(); for (Unit u : units) { String name = u.getUnit().replaceAll("\\W", "").trim(); if (str.equalsIgnoreCase(name)) return u; } return null; } /** * Set the default unit based on the unit name. Throws an exception if a * unit with the provided name is not available. * * @param name the unit name. * @throws IllegalArgumentException if the corresponding unit is not found in the group. */ public void setDefaultUnit(String name) throws IllegalArgumentException { for (int i = 0; i < units.size(); i++) { if (units.get(i).getUnit().equals(name)) { setDefaultUnit(i); return; } } throw new IllegalArgumentException("name=" + name); } public Unit getUnit(String name) throws IllegalArgumentException { for (int i = 0; i < units.size(); i++) { if (units.get(i).getUnit().equals(name)) { return units.get(i); } } throw new IllegalArgumentException("name=" + name); } public Unit getUnit(int n) { return units.get(n); } public int getUnitIndex(Unit u) { return units.indexOf(u); } public void addUnit(Unit u) { units.add(u); } public boolean contains(Unit u) { return units.contains(u); } public Unit[] getUnits() { return units.toArray(new Unit[0]); } /** * Return the value in SI units from the default unit of this group. * It is the same as calling <code>getDefaultUnit().fromUnit(value)</code> * * @param value the default unit value to convert * @return the value in SI units. * @see Unit#fromUnit(double) */ public double fromUnit(double value) { return this.getDefaultUnit().fromUnit(value); } /** * Return the value formatted by the default unit of this group. * It is the same as calling <code>getDefaultUnit().toString(value)</code>. * * @param value the SI value to format. * @return the formatted string. * @see Unit#toString(double) */ public String toString(double value) { return this.getDefaultUnit().toString(value); } /** * Return the value formatted by the default unit of this group including the unit. * It is the same as calling <code>getDefaultUnit().toStringUnit(value)</code>. * * @param value the SI value to format. * @return the formatted string. * @see Unit#toStringUnit(double) */ public String toStringUnit(double value) { return this.getDefaultUnit().toStringUnit(value); } /** * Creates a new Value object with the specified value and the default unit of this group. * * @param value the value to set. * @return a new Value object. */ public Value toValue(double value) { return this.getDefaultUnit().toValue(value); } @Override public String toString() { return this.getClass().getSimpleName() + ":" + this.getSIUnit().toString(); } @Override public boolean equals(Object o) { UnitGroup u = (UnitGroup) o; int size = units.size(); if (size != u.units.size()) { return false; } for (int i = 0; i < size; i++) { if (!units.get(i).equals(u.units.get(i))) { return false; } } return true; } private static final Pattern STRING_PATTERN = Pattern.compile("^\\s*([0-9.,-]+)(.*?)$"); /** * Converts a string into an SI value. If the string has one of the units in this * group appended to it, that unit will be used in conversion. Otherwise the default * unit will be used. If an unknown unit is specified or the value does not parse * with <code>Double.parseDouble</code> then a <code>NumberFormatException</code> * is thrown. * <p> * This method is applicable only for simple units without e.g. powers. * * @param str the string to parse. * @return the SI value. * @throws NumberFormatException if the string cannot be parsed. */ public double fromString(String str) { Matcher matcher = STRING_PATTERN.matcher(str); if (!matcher.matches()) { throw new NumberFormatException("string did not match required pattern"); } double value = Double.parseDouble(matcher.group(1)); String unit = matcher.group(2).trim(); if (unit.equals("")) { value = this.getDefaultUnit().fromUnit(value); } else { int i; for (i = 0; i < units.size(); i++) { Unit u = units.get(i); if (unit.equalsIgnoreCase(u.getUnit())) { value = u.fromUnit(value); break; } } if (i >= units.size()) { throw new NumberFormatException("unknown unit " + unit); } } return value; } /////////////////////////// @Override public int hashCode() { int code = 0; for (Unit u : units) { code = code + u.hashCode(); } return code; } /** * A private class that switches the CaliberUnit to a rocket-specific CaliberUnit. * All other methods are passed through to UNITS_STABILITY. */ private static class StabilityUnitGroup extends UnitGroup { public StabilityUnitGroup(double ref) { this(new CaliberUnit(ref)); } public StabilityUnitGroup(Rocket rocket) { this(new CaliberUnit(rocket)); } public StabilityUnitGroup(Configuration config) { this(new CaliberUnit(config)); } private StabilityUnitGroup(CaliberUnit caliberUnit) { this.units.addAll(UnitGroup.UNITS_STABILITY.units); this.defaultUnit = UnitGroup.UNITS_STABILITY.defaultUnit; for (int i = 0; i < units.size(); i++) { if (units.get(i) instanceof CaliberUnit) { units.set(i, caliberUnit); } } } @Override public void setDefaultUnit(int n) { super.setDefaultUnit(n); UNITS_STABILITY.setDefaultUnit(n); } } }