1 package net.sf.openrocket.unit;
3 import static net.sf.openrocket.util.Chars.*;
4 import static net.sf.openrocket.util.MathUtil.pow2;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.HashMap;
10 import java.util.regex.Matcher;
11 import java.util.regex.Pattern;
13 import net.sf.openrocket.rocketcomponent.Configuration;
14 import net.sf.openrocket.rocketcomponent.Rocket;
18 * A group of units (eg. length, mass etc.). Contains a list of different units of a same
21 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
24 public class UnitGroup {
26 public static final UnitGroup UNITS_NONE;
28 public static final UnitGroup UNITS_MOTOR_DIMENSIONS;
29 public static final UnitGroup UNITS_LENGTH;
30 public static final UnitGroup UNITS_DISTANCE;
32 public static final UnitGroup UNITS_AREA;
33 public static final UnitGroup UNITS_STABILITY;
34 public static final UnitGroup UNITS_VELOCITY;
35 public static final UnitGroup UNITS_ACCELERATION;
36 public static final UnitGroup UNITS_MASS;
37 public static final UnitGroup UNITS_ANGLE;
38 public static final UnitGroup UNITS_DENSITY_BULK;
39 public static final UnitGroup UNITS_DENSITY_SURFACE;
40 public static final UnitGroup UNITS_DENSITY_LINE;
41 public static final UnitGroup UNITS_FORCE;
42 public static final UnitGroup UNITS_IMPULSE;
44 /** Time in the order of less than a second (time step etc). */
45 public static final UnitGroup UNITS_TIME_STEP;
47 /** Time in the order of seconds (motor delay etc). */
48 public static final UnitGroup UNITS_SHORT_TIME;
50 /** Time in the order of the flight time of a rocket. */
51 public static final UnitGroup UNITS_FLIGHT_TIME;
52 public static final UnitGroup UNITS_ROLL;
53 public static final UnitGroup UNITS_TEMPERATURE;
54 public static final UnitGroup UNITS_PRESSURE;
55 public static final UnitGroup UNITS_RELATIVE;
56 public static final UnitGroup UNITS_ROUGHNESS;
58 public static final UnitGroup UNITS_COEFFICIENT;
60 // public static final UnitGroup UNITS_FREQUENCY;
63 public static final Map<String, UnitGroup> UNITS;
67 * Note: Units may not use HTML tags.
70 UNITS_NONE = new UnitGroup();
71 UNITS_NONE.addUnit(Unit.NOUNIT2);
73 UNITS_LENGTH = new UnitGroup();
74 UNITS_LENGTH.addUnit(new GeneralUnit(0.001,"mm"));
75 UNITS_LENGTH.addUnit(new GeneralUnit(0.01,"cm"));
76 UNITS_LENGTH.addUnit(new GeneralUnit(1,"m"));
77 UNITS_LENGTH.addUnit(new GeneralUnit(0.0254,"in"));
78 UNITS_LENGTH.addUnit(new GeneralUnit(0.3048,"ft"));
79 UNITS_LENGTH.setDefaultUnit(1);
81 UNITS_MOTOR_DIMENSIONS = new UnitGroup();
82 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001,"mm"));
83 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01,"cm"));
84 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254,"in"));
85 UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0);
87 UNITS_DISTANCE = new UnitGroup();
88 UNITS_DISTANCE.addUnit(new GeneralUnit(1,"m"));
89 UNITS_DISTANCE.addUnit(new GeneralUnit(1000,"km"));
90 UNITS_DISTANCE.addUnit(new GeneralUnit(0.3048,"ft"));
91 UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144,"yd"));
92 UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344,"mi"));
94 UNITS_AREA = new UnitGroup();
95 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001),"mm" + SQUARED));
96 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01),"cm" + SQUARED));
97 UNITS_AREA.addUnit(new GeneralUnit(1,"m" + SQUARED));
98 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254),"in" + SQUARED));
99 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048),"ft" + SQUARED));
100 UNITS_AREA.setDefaultUnit(1);
103 UNITS_STABILITY = new UnitGroup();
104 UNITS_STABILITY.addUnit(new GeneralUnit(0.001,"mm"));
105 UNITS_STABILITY.addUnit(new GeneralUnit(0.01,"cm"));
106 UNITS_STABILITY.addUnit(new GeneralUnit(0.0254,"in"));
107 UNITS_STABILITY.addUnit(new CaliberUnit((Rocket)null));
108 UNITS_STABILITY.setDefaultUnit(3);
110 UNITS_VELOCITY = new UnitGroup();
111 UNITS_VELOCITY.addUnit(new GeneralUnit(1, "m/s"));
112 UNITS_VELOCITY.addUnit(new GeneralUnit(1/3.6, "km/h"));
113 UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s"));
114 UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph"));
116 UNITS_ACCELERATION = new UnitGroup();
117 UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED));
118 UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED));
121 UNITS_MASS = new UnitGroup();
122 UNITS_MASS.addUnit(new GeneralUnit(0.001,"g"));
123 UNITS_MASS.addUnit(new GeneralUnit(1,"kg"));
124 UNITS_MASS.addUnit(new GeneralUnit(0.0283495231,"oz"));
125 UNITS_MASS.addUnit(new GeneralUnit(0.45359237,"lb"));
127 UNITS_ANGLE = new UnitGroup();
128 UNITS_ANGLE.addUnit(new DegreeUnit());
129 UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad",0.01));
131 UNITS_DENSITY_BULK = new UnitGroup();
132 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000,"g/cm" + CUBED));
133 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1,"kg/m" + CUBED));
134 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404,"oz/in" + CUBED));
135 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634,"lb/ft" + CUBED));
137 UNITS_DENSITY_SURFACE = new UnitGroup();
138 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10,"g/cm" + SQUARED));
139 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001,"g/m" + SQUARED));
140 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1,"kg/m" + SQUARED));
141 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487,"oz/in" + SQUARED));
142 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727,"oz/ft" + SQUARED));
143 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764,"lb/ft" + SQUARED));
144 UNITS_DENSITY_SURFACE.setDefaultUnit(1);
146 UNITS_DENSITY_LINE = new UnitGroup();
147 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001,"g/m"));
148 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1,"kg/m"));
149 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465,"oz/ft"));
151 UNITS_FORCE = new UnitGroup();
152 UNITS_FORCE.addUnit(new GeneralUnit(1,"N"));
153 UNITS_FORCE.addUnit(new GeneralUnit(4.44822162,"lbf"));
154 UNITS_FORCE.addUnit(new GeneralUnit(9.80665,"kgf"));
156 UNITS_IMPULSE = new UnitGroup();
157 UNITS_IMPULSE.addUnit(new GeneralUnit(1,"Ns"));
158 UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf"+DOT+"s"));
160 UNITS_TIME_STEP = new UnitGroup();
161 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001));
162 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("s", 0.01));
163 UNITS_TIME_STEP.setDefaultUnit(1);
165 UNITS_SHORT_TIME = new UnitGroup();
166 UNITS_SHORT_TIME.addUnit(new GeneralUnit(1,"s"));
168 UNITS_FLIGHT_TIME = new UnitGroup();
169 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1,"s"));
170 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60,"min"));
172 UNITS_ROLL = new UnitGroup();
173 UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s"));
174 UNITS_ROLL.addUnit(new GeneralUnit(2*Math.PI, "r/s"));
175 UNITS_ROLL.addUnit(new GeneralUnit(2*Math.PI/60, "rpm"));
176 UNITS_ROLL.setDefaultUnit(1);
178 UNITS_TEMPERATURE = new UnitGroup();
179 UNITS_TEMPERATURE.addUnit(new FixedPrecisionUnit("K", 1));
180 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, DEGREE+"C"));
181 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0/9.0, 459.67, DEGREE+"F"));
182 UNITS_TEMPERATURE.setDefaultUnit(1);
184 UNITS_PRESSURE = new UnitGroup();
185 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("mbar", 1, 1.0e2));
186 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("bar", 0.001, 1.0e5));
187 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("atm", 0.001, 1.01325e5));
188 UNITS_PRESSURE.addUnit(new GeneralUnit(101325.0/760.0, "mmHg"));
189 UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg"));
190 UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi"));
191 UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa"));
193 UNITS_RELATIVE = new UnitGroup();
194 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01, 1.0));
195 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01));
196 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit(""+PERMILLE, 1, 0.001));
197 UNITS_RELATIVE.setDefaultUnit(1);
200 UNITS_ROUGHNESS = new UnitGroup();
201 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO+"m"));
202 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil"));
205 UNITS_COEFFICIENT = new UnitGroup();
206 UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01)); // zero-width space
209 // This is not used by OpenRocket, and not extensively tested:
210 // UNITS_FREQUENCY = new UnitGroup();
211 // UNITS_FREQUENCY.addUnit(new GeneralUnit(1, "s"));
212 // UNITS_FREQUENCY.addUnit(new GeneralUnit(0.001, "ms"));
213 // UNITS_FREQUENCY.addUnit(new GeneralUnit(0.000001, MICRO + "s"));
214 // UNITS_FREQUENCY.addUnit(new FrequencyUnit(1, "Hz"));
215 // UNITS_FREQUENCY.addUnit(new FrequencyUnit(1000, "kHz"));
216 // UNITS_FREQUENCY.setDefaultUnit(3);
219 HashMap<String,UnitGroup> map = new HashMap<String,UnitGroup>();
220 map.put("NONE", UNITS_NONE);
221 map.put("LENGTH", UNITS_LENGTH);
222 map.put("MOTOR_DIMENSIONS", UNITS_MOTOR_DIMENSIONS);
223 map.put("DISTANCE", UNITS_DISTANCE);
224 map.put("VELOCITY", UNITS_VELOCITY);
225 map.put("ACCELERATION", UNITS_ACCELERATION);
226 map.put("AREA", UNITS_AREA);
227 map.put("STABILITY", UNITS_STABILITY);
228 map.put("MASS", UNITS_MASS);
229 map.put("ANGLE", UNITS_ANGLE);
230 map.put("DENSITY_BULK", UNITS_DENSITY_BULK);
231 map.put("DENSITY_SURFACE", UNITS_DENSITY_SURFACE);
232 map.put("DENSITY_LINE", UNITS_DENSITY_LINE);
233 map.put("FORCE", UNITS_FORCE);
234 map.put("IMPULSE", UNITS_IMPULSE);
235 map.put("TIME_STEP", UNITS_TIME_STEP);
236 map.put("SHORT_TIME", UNITS_SHORT_TIME);
237 map.put("FLIGHT_TIME", UNITS_FLIGHT_TIME);
238 map.put("ROLL", UNITS_ROLL);
239 map.put("TEMPERATURE", UNITS_TEMPERATURE);
240 map.put("PRESSURE", UNITS_PRESSURE);
241 map.put("RELATIVE", UNITS_RELATIVE);
242 map.put("ROUGHNESS", UNITS_ROUGHNESS);
243 map.put("COEFFICIENT", UNITS_COEFFICIENT);
245 UNITS = Collections.unmodifiableMap(map);
248 public static void setDefaultMetricUnits() {
249 UNITS_LENGTH.setDefaultUnit("cm");
250 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("mm");
251 UNITS_DISTANCE.setDefaultUnit("m");
252 UNITS_AREA.setDefaultUnit("cm"+SQUARED);
253 UNITS_STABILITY.setDefaultUnit("cal");
254 UNITS_VELOCITY.setDefaultUnit("m/s");
255 UNITS_ACCELERATION.setDefaultUnit("m/s"+SQUARED);
256 UNITS_MASS.setDefaultUnit("g");
257 UNITS_ANGLE.setDefaultUnit(""+DEGREE);
258 UNITS_DENSITY_BULK.setDefaultUnit("g/cm"+CUBED);
259 UNITS_DENSITY_SURFACE.setDefaultUnit("g/m"+SQUARED);
260 UNITS_DENSITY_LINE.setDefaultUnit("g/m");
261 UNITS_FORCE.setDefaultUnit("N");
262 UNITS_IMPULSE.setDefaultUnit("Ns");
263 UNITS_TIME_STEP.setDefaultUnit("s");
264 UNITS_FLIGHT_TIME.setDefaultUnit("s");
265 UNITS_ROLL.setDefaultUnit("r/s");
266 UNITS_TEMPERATURE.setDefaultUnit(DEGREE+"C");
267 UNITS_PRESSURE.setDefaultUnit("mbar");
268 UNITS_RELATIVE.setDefaultUnit("%");
269 UNITS_ROUGHNESS.setDefaultUnit(MICRO+"m");
272 public static void setDefaultImperialUnits() {
273 UNITS_LENGTH.setDefaultUnit("in");
274 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("in");
275 UNITS_DISTANCE.setDefaultUnit("ft");
276 UNITS_AREA.setDefaultUnit("in"+SQUARED);
277 UNITS_STABILITY.setDefaultUnit("cal");
278 UNITS_VELOCITY.setDefaultUnit("ft/s");
279 UNITS_ACCELERATION.setDefaultUnit("ft/s"+SQUARED);
280 UNITS_MASS.setDefaultUnit("oz");
281 UNITS_ANGLE.setDefaultUnit(""+DEGREE);
282 UNITS_DENSITY_BULK.setDefaultUnit("oz/in"+CUBED);
283 UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft"+SQUARED);
284 UNITS_DENSITY_LINE.setDefaultUnit("oz/ft");
285 UNITS_FORCE.setDefaultUnit("N");
286 UNITS_IMPULSE.setDefaultUnit("Ns");
287 UNITS_TIME_STEP.setDefaultUnit("s");
288 UNITS_FLIGHT_TIME.setDefaultUnit("s");
289 UNITS_ROLL.setDefaultUnit("r/s");
290 UNITS_TEMPERATURE.setDefaultUnit(DEGREE+"F");
291 UNITS_PRESSURE.setDefaultUnit("mbar");
292 UNITS_RELATIVE.setDefaultUnit("%");
293 UNITS_ROUGHNESS.setDefaultUnit("mil");
298 public static UnitGroup stabilityUnits(Rocket rocket) {
299 return new StabilityUnitGroup(rocket);
303 public static UnitGroup stabilityUnits(Configuration config) {
304 return new StabilityUnitGroup(config);
308 //////////////////////////////////////////////////////
311 private ArrayList<Unit> units = new ArrayList<Unit>();
312 private int defaultUnit = 0;
314 public int getUnitCount() {
318 public Unit getDefaultUnit() {
319 return units.get(defaultUnit);
322 public int getDefaultUnitIndex() {
326 public void setDefaultUnit(int n) {
327 if (n<0 || n>=units.size()) {
328 throw new IllegalArgumentException("index out of range: "+n);
336 * Find a unit by approximate unit name. Only letters and (ordinary) numbers are
337 * considered in the matching. This method is mainly means for testing, allowing
338 * a simple means to obtain a particular unit.
340 * @param str the unit name.
341 * @return the corresponding unit, or <code>null</code> if not found.
343 public Unit findApproximate(String str) {
344 str = str.replaceAll("\\W", "").trim();
345 for (Unit u: units) {
346 String name = u.getUnit().replaceAll("\\W", "").trim();
347 if (str.equalsIgnoreCase(name))
354 * Set the default unit based on the unit name. Throws an exception if a
355 * unit with the provided name is not available.
357 * @param name the unit name.
358 * @throws IllegalArgumentException if the corresponding unit is not found in the group.
360 public void setDefaultUnit(String name) throws IllegalArgumentException {
361 for (int i=0; i < units.size(); i++) {
362 if (units.get(i).getUnit().equals(name)) {
367 throw new IllegalArgumentException("name="+name);
371 public Unit getUnit(int n) {
375 public int getUnitIndex(Unit u) {
376 return units.indexOf(u);
379 public void addUnit(Unit u) {
383 public void addUnit(int n, Unit u) {
387 public void removeUnit(int n) {
391 public boolean contains(Unit u) {
392 return units.contains(u);
395 public Unit[] getUnits() {
396 return units.toArray(new Unit[0]);
401 * Return the value formatted by the default unit of this group.
402 * It is the same as calling <code>getDefaultUnit().toString(value)</code>.
404 * @param value the SI value to format.
405 * @return the formatted string.
406 * @see Unit#toString(double)
408 public String toString(double value) {
409 return this.getDefaultUnit().toString(value);
414 * Return the value formatted by the default unit of this group including the unit.
415 * It is the same as calling <code>getDefaultUnit().toStringUnit(value)</code>.
417 * @param value the SI value to format.
418 * @return the formatted string.
419 * @see Unit#toStringUnit(double)
421 public String toStringUnit(double value) {
422 return this.getDefaultUnit().toStringUnit(value);
430 * Creates a new Value object with the specified value and the default unit of this group.
432 * @param value the value to set.
433 * @return a new Value object.
435 public Value toValue(double value) {
436 return this.getDefaultUnit().toValue(value);
442 private static final Pattern STRING_PATTERN = Pattern.compile("^\\s*([0-9.,-]+)(.*?)$");
444 * Converts a string into an SI value. If the string has one of the units in this
445 * group appended to it, that unit will be used in conversion. Otherwise the default
446 * unit will be used. If an unknown unit is specified or the value does not parse
447 * with <code>Double.parseDouble</code> then a <code>NumberFormatException</code>
450 * This method is applicable only for simple units without e.g. powers.
452 * @param str the string to parse.
453 * @return the SI value.
454 * @throws NumberFormatException if the string cannot be parsed.
456 public double fromString(String str) {
457 Matcher matcher = STRING_PATTERN.matcher(str);
459 if (!matcher.matches()) {
460 throw new NumberFormatException("string did not match required pattern");
463 double value = Double.parseDouble(matcher.group(1));
464 String unit = matcher.group(2).trim();
466 if (unit.equals("")) {
467 value = this.getDefaultUnit().fromUnit(value);
470 for (i=0; i < units.size(); i++) {
471 Unit u = units.get(i);
472 if (unit.equalsIgnoreCase(u.getUnit())) {
473 value = u.fromUnit(value);
477 if (i >= units.size()) {
478 throw new NumberFormatException("unknown unit "+unit);
486 ///////////////////////////
490 * A private class that switches the CaliberUnit to a rocket-specific CaliberUnit.
491 * All other methods are passed through to UNITS_STABILITY.
493 private static class StabilityUnitGroup extends UnitGroup {
495 private final CaliberUnit caliberUnit;
498 public StabilityUnitGroup(Rocket rocket) {
499 caliberUnit = new CaliberUnit(rocket);
502 public StabilityUnitGroup(Configuration config) {
503 caliberUnit = new CaliberUnit(config);
507 //// Modify CaliberUnit to use local variable
510 public Unit getDefaultUnit() {
511 return getUnit(UNITS_STABILITY.getDefaultUnitIndex());
515 public Unit getUnit(int n) {
516 Unit u = UNITS_STABILITY.getUnit(n);
517 if (u instanceof CaliberUnit) {
524 public int getUnitIndex(Unit u) {
525 if (u instanceof CaliberUnit) {
526 for (int i=0; i < UNITS_STABILITY.getUnitCount(); i++) {
527 if (UNITS_STABILITY.getUnit(i) instanceof CaliberUnit)
531 return UNITS_STABILITY.getUnitIndex(u);
536 //// Pass on to UNITS_STABILITY
539 public int getDefaultUnitIndex() {
540 return UNITS_STABILITY.getDefaultUnitIndex();
544 public void setDefaultUnit(int n) {
545 UNITS_STABILITY.setDefaultUnit(n);
549 public int getUnitCount() {
550 return UNITS_STABILITY.getUnitCount();
554 //// Unsupported methods
557 public void addUnit(int n, Unit u) {
558 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");
562 public void addUnit(Unit u) {
563 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");
567 public void removeUnit(int n) {
568 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");