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;
61 public static final Map<String, UnitGroup> UNITS;
65 * Note: Units may not use HTML tags.
68 UNITS_NONE = new UnitGroup();
69 UNITS_NONE.addUnit(Unit.NOUNIT2);
71 UNITS_LENGTH = new UnitGroup();
72 UNITS_LENGTH.addUnit(new GeneralUnit(0.001,"mm"));
73 UNITS_LENGTH.addUnit(new GeneralUnit(0.01,"cm"));
74 UNITS_LENGTH.addUnit(new GeneralUnit(1,"m"));
75 UNITS_LENGTH.addUnit(new GeneralUnit(0.0254,"in"));
76 UNITS_LENGTH.addUnit(new GeneralUnit(0.3048,"ft"));
77 UNITS_LENGTH.setDefaultUnit(1);
79 UNITS_MOTOR_DIMENSIONS = new UnitGroup();
80 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001,"mm"));
81 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01,"cm"));
82 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254,"in"));
83 UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0);
85 UNITS_DISTANCE = new UnitGroup();
86 UNITS_DISTANCE.addUnit(new GeneralUnit(1,"m"));
87 UNITS_DISTANCE.addUnit(new GeneralUnit(1000,"km"));
88 UNITS_DISTANCE.addUnit(new GeneralUnit(0.3048,"ft"));
89 UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144,"yd"));
90 UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344,"mi"));
92 UNITS_AREA = new UnitGroup();
93 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001),"mm" + SQUARED));
94 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01),"cm" + SQUARED));
95 UNITS_AREA.addUnit(new GeneralUnit(1,"m" + SQUARED));
96 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254),"in" + SQUARED));
97 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048),"ft" + SQUARED));
98 UNITS_AREA.setDefaultUnit(1);
101 UNITS_STABILITY = new UnitGroup();
102 UNITS_STABILITY.addUnit(new GeneralUnit(0.001,"mm"));
103 UNITS_STABILITY.addUnit(new GeneralUnit(0.01,"cm"));
104 UNITS_STABILITY.addUnit(new GeneralUnit(0.0254,"in"));
105 UNITS_STABILITY.addUnit(new CaliberUnit((Rocket)null));
106 UNITS_STABILITY.setDefaultUnit(3);
108 UNITS_VELOCITY = new UnitGroup();
109 UNITS_VELOCITY.addUnit(new GeneralUnit(1, "m/s"));
110 UNITS_VELOCITY.addUnit(new GeneralUnit(1/3.6, "km/h"));
111 UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s"));
112 UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph"));
114 UNITS_ACCELERATION = new UnitGroup();
115 UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED));
116 UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED));
119 UNITS_MASS = new UnitGroup();
120 UNITS_MASS.addUnit(new GeneralUnit(0.001,"g"));
121 UNITS_MASS.addUnit(new GeneralUnit(1,"kg"));
122 UNITS_MASS.addUnit(new GeneralUnit(0.0283495231,"oz"));
123 UNITS_MASS.addUnit(new GeneralUnit(0.45359237,"lb"));
125 UNITS_ANGLE = new UnitGroup();
126 UNITS_ANGLE.addUnit(new DegreeUnit());
127 UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad",0.01));
129 UNITS_DENSITY_BULK = new UnitGroup();
130 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000,"g/cm" + CUBED));
131 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1,"kg/m" + CUBED));
132 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404,"oz/in" + CUBED));
133 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634,"lb/ft" + CUBED));
135 UNITS_DENSITY_SURFACE = new UnitGroup();
136 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10,"g/cm" + SQUARED));
137 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001,"g/m" + SQUARED));
138 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1,"kg/m" + SQUARED));
139 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487,"oz/in" + SQUARED));
140 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727,"oz/ft" + SQUARED));
141 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764,"lb/ft" + SQUARED));
142 UNITS_DENSITY_SURFACE.setDefaultUnit(1);
144 UNITS_DENSITY_LINE = new UnitGroup();
145 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001,"g/m"));
146 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1,"kg/m"));
147 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465,"oz/ft"));
149 UNITS_FORCE = new UnitGroup();
150 UNITS_FORCE.addUnit(new GeneralUnit(1,"N"));
151 UNITS_FORCE.addUnit(new GeneralUnit(4.44822162,"lbf"));
152 UNITS_FORCE.addUnit(new GeneralUnit(9.80665,"kgf"));
154 UNITS_IMPULSE = new UnitGroup();
155 UNITS_IMPULSE.addUnit(new GeneralUnit(1,"Ns"));
156 UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf"+DOT+"s"));
158 UNITS_TIME_STEP = new UnitGroup();
159 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001));
160 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("s", 0.01));
161 UNITS_TIME_STEP.setDefaultUnit(1);
163 UNITS_SHORT_TIME = new UnitGroup();
164 UNITS_SHORT_TIME.addUnit(new GeneralUnit(1,"s"));
166 UNITS_FLIGHT_TIME = new UnitGroup();
167 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1,"s"));
168 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60,"min"));
170 UNITS_ROLL = new UnitGroup();
171 UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s"));
172 UNITS_ROLL.addUnit(new GeneralUnit(2*Math.PI, "r/s"));
173 UNITS_ROLL.addUnit(new GeneralUnit(2*Math.PI/60, "rpm"));
174 UNITS_ROLL.setDefaultUnit(1);
176 UNITS_TEMPERATURE = new UnitGroup();
177 UNITS_TEMPERATURE.addUnit(new FixedPrecisionUnit("K", 1));
178 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, DEGREE+"C"));
179 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0/9.0, 459.67, DEGREE+"F"));
180 UNITS_TEMPERATURE.setDefaultUnit(1);
182 UNITS_PRESSURE = new UnitGroup();
183 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("mbar", 1, 1.0e2));
184 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("bar", 0.001, 1.0e5));
185 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("atm", 0.001, 1.01325e5));
186 UNITS_PRESSURE.addUnit(new GeneralUnit(101325.0/760.0, "mmHg"));
187 UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg"));
188 UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi"));
189 UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa"));
191 UNITS_RELATIVE = new UnitGroup();
192 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01, 1.0));
193 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01));
194 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit(""+PERMILLE, 1, 0.001));
195 UNITS_RELATIVE.setDefaultUnit(1);
198 UNITS_ROUGHNESS = new UnitGroup();
199 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO+"m"));
200 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil"));
203 UNITS_COEFFICIENT = new UnitGroup();
204 UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01)); // zero-width space
207 HashMap<String,UnitGroup> map = new HashMap<String,UnitGroup>();
208 map.put("NONE", UNITS_NONE);
209 map.put("LENGTH", UNITS_LENGTH);
210 map.put("MOTOR_DIMENSIONS", UNITS_MOTOR_DIMENSIONS);
211 map.put("DISTANCE", UNITS_DISTANCE);
212 map.put("VELOCITY", UNITS_VELOCITY);
213 map.put("ACCELERATION", UNITS_ACCELERATION);
214 map.put("AREA", UNITS_AREA);
215 map.put("STABILITY", UNITS_STABILITY);
216 map.put("MASS", UNITS_MASS);
217 map.put("ANGLE", UNITS_ANGLE);
218 map.put("DENSITY_BULK", UNITS_DENSITY_BULK);
219 map.put("DENSITY_SURFACE", UNITS_DENSITY_SURFACE);
220 map.put("DENSITY_LINE", UNITS_DENSITY_LINE);
221 map.put("FORCE", UNITS_FORCE);
222 map.put("IMPULSE", UNITS_IMPULSE);
223 map.put("TIME_STEP", UNITS_TIME_STEP);
224 map.put("SHORT_TIME", UNITS_SHORT_TIME);
225 map.put("FLIGHT_TIME", UNITS_FLIGHT_TIME);
226 map.put("ROLL", UNITS_ROLL);
227 map.put("TEMPERATURE", UNITS_TEMPERATURE);
228 map.put("PRESSURE", UNITS_PRESSURE);
229 map.put("RELATIVE", UNITS_RELATIVE);
230 map.put("ROUGHNESS", UNITS_ROUGHNESS);
231 map.put("COEFFICIENT", UNITS_COEFFICIENT);
233 UNITS = Collections.unmodifiableMap(map);
236 public static void setDefaultMetricUnits() {
237 UNITS_LENGTH.setDefaultUnit("cm");
238 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("mm");
239 UNITS_DISTANCE.setDefaultUnit("m");
240 UNITS_AREA.setDefaultUnit("cm"+SQUARED);
241 UNITS_STABILITY.setDefaultUnit("cal");
242 UNITS_VELOCITY.setDefaultUnit("m/s");
243 UNITS_ACCELERATION.setDefaultUnit("m/s"+SQUARED);
244 UNITS_MASS.setDefaultUnit("g");
245 UNITS_ANGLE.setDefaultUnit(""+DEGREE);
246 UNITS_DENSITY_BULK.setDefaultUnit("g/cm"+CUBED);
247 UNITS_DENSITY_SURFACE.setDefaultUnit("g/m"+SQUARED);
248 UNITS_DENSITY_LINE.setDefaultUnit("g/m");
249 UNITS_FORCE.setDefaultUnit("N");
250 UNITS_IMPULSE.setDefaultUnit("Ns");
251 UNITS_TIME_STEP.setDefaultUnit("s");
252 UNITS_FLIGHT_TIME.setDefaultUnit("s");
253 UNITS_ROLL.setDefaultUnit("r/s");
254 UNITS_TEMPERATURE.setDefaultUnit(DEGREE+"C");
255 UNITS_PRESSURE.setDefaultUnit("mbar");
256 UNITS_RELATIVE.setDefaultUnit("%");
257 UNITS_ROUGHNESS.setDefaultUnit(MICRO+"m");
260 public static void setDefaultImperialUnits() {
261 UNITS_LENGTH.setDefaultUnit("in");
262 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("in");
263 UNITS_DISTANCE.setDefaultUnit("ft");
264 UNITS_AREA.setDefaultUnit("in"+SQUARED);
265 UNITS_STABILITY.setDefaultUnit("cal");
266 UNITS_VELOCITY.setDefaultUnit("ft/s");
267 UNITS_ACCELERATION.setDefaultUnit("ft/s"+SQUARED);
268 UNITS_MASS.setDefaultUnit("oz");
269 UNITS_ANGLE.setDefaultUnit(""+DEGREE);
270 UNITS_DENSITY_BULK.setDefaultUnit("oz/in"+CUBED);
271 UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft"+SQUARED);
272 UNITS_DENSITY_LINE.setDefaultUnit("oz/ft");
273 UNITS_FORCE.setDefaultUnit("N");
274 UNITS_IMPULSE.setDefaultUnit("Ns");
275 UNITS_TIME_STEP.setDefaultUnit("s");
276 UNITS_FLIGHT_TIME.setDefaultUnit("s");
277 UNITS_ROLL.setDefaultUnit("r/s");
278 UNITS_TEMPERATURE.setDefaultUnit(DEGREE+"F");
279 UNITS_PRESSURE.setDefaultUnit("mbar");
280 UNITS_RELATIVE.setDefaultUnit("%");
281 UNITS_ROUGHNESS.setDefaultUnit("mil");
286 public static UnitGroup stabilityUnits(Rocket rocket) {
287 return new StabilityUnitGroup(rocket);
291 public static UnitGroup stabilityUnits(Configuration config) {
292 return new StabilityUnitGroup(config);
296 //////////////////////////////////////////////////////
299 private ArrayList<Unit> units = new ArrayList<Unit>();
300 private int defaultUnit = 0;
302 public int getUnitCount() {
306 public Unit getDefaultUnit() {
307 return units.get(defaultUnit);
310 public int getDefaultUnitIndex() {
314 public void setDefaultUnit(int n) {
315 if (n<0 || n>=units.size()) {
316 throw new IllegalArgumentException("index out of range: "+n);
324 * Find a unit by approximate unit name. Only letters and (ordinary) numbers are
325 * considered in the matching. This method is mainly means for testing, allowing
326 * a simple means to obtain a particular unit.
328 * @param str the unit name.
329 * @return the corresponding unit, or <code>null</code> if not found.
331 public Unit findApproximate(String str) {
332 str = str.replaceAll("\\W", "").trim();
333 for (Unit u: units) {
334 String name = u.getUnit().replaceAll("\\W", "").trim();
335 if (str.equalsIgnoreCase(name))
342 * Set the default unit based on the unit name. Throws an exception if a
343 * unit with the provided name is not available.
345 * @param name the unit name.
346 * @throws IllegalArgumentException if the corresponding unit is not found in the group.
348 public void setDefaultUnit(String name) throws IllegalArgumentException {
349 for (int i=0; i < units.size(); i++) {
350 if (units.get(i).getUnit().equals(name)) {
355 throw new IllegalArgumentException("name="+name);
359 public Unit getUnit(int n) {
363 public int getUnitIndex(Unit u) {
364 return units.indexOf(u);
367 public void addUnit(Unit u) {
371 public void addUnit(int n, Unit u) {
375 public void removeUnit(int n) {
379 public boolean contains(Unit u) {
380 return units.contains(u);
383 public Unit[] getUnits() {
384 return units.toArray(new Unit[0]);
389 * Return the value formatted by the default unit of this group.
390 * It is the same as calling <code>getDefaultUnit().toString(value)</code>.
392 * @param value the SI value to format.
393 * @return the formatted string.
394 * @see Unit#toString(double)
396 public String toString(double value) {
397 return this.getDefaultUnit().toString(value);
402 * Return the value formatted by the default unit of this group including the unit.
403 * It is the same as calling <code>getDefaultUnit().toStringUnit(value)</code>.
405 * @param value the SI value to format.
406 * @return the formatted string.
407 * @see Unit#toStringUnit(double)
409 public String toStringUnit(double value) {
410 return this.getDefaultUnit().toStringUnit(value);
418 * Creates a new Value object with the specified value and the default unit of this group.
420 * @param value the value to set.
421 * @return a new Value object.
423 public Value toValue(double value) {
424 return this.getDefaultUnit().toValue(value);
430 private static final Pattern STRING_PATTERN = Pattern.compile("^\\s*([0-9.,-]+)(.*?)$");
432 * Converts a string into an SI value. If the string has one of the units in this
433 * group appended to it, that unit will be used in conversion. Otherwise the default
434 * unit will be used. If an unknown unit is specified or the value does not parse
435 * with <code>Double.parseDouble</code> then a <code>NumberFormatException</code>
438 * This method is applicable only for simple units without e.g. powers.
440 * @param str the string to parse.
441 * @return the SI value.
442 * @throws NumberFormatException if the string cannot be parsed.
444 public double fromString(String str) {
445 Matcher matcher = STRING_PATTERN.matcher(str);
447 if (!matcher.matches()) {
448 throw new NumberFormatException("string did not match required pattern");
451 double value = Double.parseDouble(matcher.group(1));
452 String unit = matcher.group(2).trim();
454 if (unit.equals("")) {
455 value = this.getDefaultUnit().fromUnit(value);
458 for (i=0; i < units.size(); i++) {
459 Unit u = units.get(i);
460 if (unit.equalsIgnoreCase(u.getUnit())) {
461 value = u.fromUnit(value);
465 if (i >= units.size()) {
466 throw new NumberFormatException("unknown unit "+unit);
474 ///////////////////////////
478 * A private class that switches the CaliberUnit to a rocket-specific CaliberUnit.
479 * All other methods are passed through to UNITS_STABILITY.
481 private static class StabilityUnitGroup extends UnitGroup {
483 private final CaliberUnit caliberUnit;
486 public StabilityUnitGroup(Rocket rocket) {
487 caliberUnit = new CaliberUnit(rocket);
490 public StabilityUnitGroup(Configuration config) {
491 caliberUnit = new CaliberUnit(config);
495 //// Modify CaliberUnit to use local variable
498 public Unit getDefaultUnit() {
499 return getUnit(UNITS_STABILITY.getDefaultUnitIndex());
503 public Unit getUnit(int n) {
504 Unit u = UNITS_STABILITY.getUnit(n);
505 if (u instanceof CaliberUnit) {
512 public int getUnitIndex(Unit u) {
513 if (u instanceof CaliberUnit) {
514 for (int i=0; i < UNITS_STABILITY.getUnitCount(); i++) {
515 if (UNITS_STABILITY.getUnit(i) instanceof CaliberUnit)
519 return UNITS_STABILITY.getUnitIndex(u);
524 //// Pass on to UNITS_STABILITY
527 public int getDefaultUnitIndex() {
528 return UNITS_STABILITY.getDefaultUnitIndex();
532 public void setDefaultUnit(int n) {
533 UNITS_STABILITY.setDefaultUnit(n);
537 public int getUnitCount() {
538 return UNITS_STABILITY.getUnitCount();
542 //// Unsupported methods
545 public void addUnit(int n, Unit u) {
546 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");
550 public void addUnit(Unit u) {
551 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");
555 public void removeUnit(int n) {
556 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");