1 package net.sf.openrocket.unit;
3 import static net.sf.openrocket.util.MathUtil.pow2;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.HashMap;
9 import java.util.regex.Matcher;
10 import java.util.regex.Pattern;
12 import net.sf.openrocket.rocketcomponent.Configuration;
13 import net.sf.openrocket.rocketcomponent.Rocket;
17 * A group of units (eg. length, mass etc.). Contains a list of different units of a same
20 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
23 public class UnitGroup {
25 public static final UnitGroup UNITS_NONE;
27 public static final UnitGroup UNITS_MOTOR_DIMENSIONS;
28 public static final UnitGroup UNITS_LENGTH;
29 public static final UnitGroup UNITS_DISTANCE;
31 public static final UnitGroup UNITS_AREA;
32 public static final UnitGroup UNITS_STABILITY;
33 public static final UnitGroup UNITS_VELOCITY;
34 public static final UnitGroup UNITS_ACCELERATION;
35 public static final UnitGroup UNITS_MASS;
36 public static final UnitGroup UNITS_ANGLE;
37 public static final UnitGroup UNITS_DENSITY_BULK;
38 public static final UnitGroup UNITS_DENSITY_SURFACE;
39 public static final UnitGroup UNITS_DENSITY_LINE;
40 public static final UnitGroup UNITS_FORCE;
41 public static final UnitGroup UNITS_IMPULSE;
43 /** Time in the order of less than a second (time step etc). */
44 public static final UnitGroup UNITS_TIME_STEP;
46 /** Time in the order of seconds (motor delay etc). */
47 public static final UnitGroup UNITS_SHORT_TIME;
49 /** Time in the order of the flight time of a rocket. */
50 public static final UnitGroup UNITS_FLIGHT_TIME;
51 public static final UnitGroup UNITS_ROLL;
52 public static final UnitGroup UNITS_TEMPERATURE;
53 public static final UnitGroup UNITS_PRESSURE;
54 public static final UnitGroup UNITS_RELATIVE;
55 public static final UnitGroup UNITS_ROUGHNESS;
57 public static final UnitGroup UNITS_COEFFICIENT;
60 public static final Map<String, UnitGroup> UNITS;
64 * Note: Units may not use HTML tags.
67 UNITS_NONE = new UnitGroup();
68 UNITS_NONE.addUnit(Unit.NOUNIT2);
70 UNITS_LENGTH = new UnitGroup();
71 UNITS_LENGTH.addUnit(new GeneralUnit(0.001,"mm"));
72 UNITS_LENGTH.addUnit(new GeneralUnit(0.01,"cm"));
73 UNITS_LENGTH.addUnit(new GeneralUnit(1,"m"));
74 UNITS_LENGTH.addUnit(new GeneralUnit(0.0254,"in"));
75 UNITS_LENGTH.addUnit(new GeneralUnit(0.3048,"ft"));
76 UNITS_LENGTH.setDefaultUnit(1);
78 UNITS_MOTOR_DIMENSIONS = new UnitGroup();
79 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001,"mm"));
80 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01,"cm"));
81 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254,"in"));
82 UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0);
84 UNITS_DISTANCE = new UnitGroup();
85 UNITS_DISTANCE.addUnit(new GeneralUnit(1,"m"));
86 UNITS_DISTANCE.addUnit(new GeneralUnit(1000,"km"));
87 UNITS_DISTANCE.addUnit(new GeneralUnit(0.3048,"ft"));
88 UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144,"yd"));
89 UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344,"mi"));
91 UNITS_AREA = new UnitGroup();
92 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001),"mm\u00b2"));
93 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01),"cm\u00b2"));
94 UNITS_AREA.addUnit(new GeneralUnit(1,"m\u00b2"));
95 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254),"in\u00b2"));
96 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048),"ft\u00b2"));
97 UNITS_AREA.setDefaultUnit(1);
100 UNITS_STABILITY = new UnitGroup();
101 UNITS_STABILITY.addUnit(new GeneralUnit(0.001,"mm"));
102 UNITS_STABILITY.addUnit(new GeneralUnit(0.01,"cm"));
103 UNITS_STABILITY.addUnit(new GeneralUnit(0.0254,"in"));
104 UNITS_STABILITY.addUnit(new CaliberUnit((Rocket)null));
105 UNITS_STABILITY.setDefaultUnit(3);
107 UNITS_VELOCITY = new UnitGroup();
108 UNITS_VELOCITY.addUnit(new GeneralUnit(1, "m/s"));
109 UNITS_VELOCITY.addUnit(new GeneralUnit(1/3.6, "km/h"));
110 UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s"));
111 UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph"));
113 UNITS_ACCELERATION = new UnitGroup();
114 UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s\u00b2"));
115 UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s\00b2"));
118 UNITS_MASS = new UnitGroup();
119 UNITS_MASS.addUnit(new GeneralUnit(0.001,"g"));
120 UNITS_MASS.addUnit(new GeneralUnit(1,"kg"));
121 UNITS_MASS.addUnit(new GeneralUnit(0.0283495231,"oz"));
122 UNITS_MASS.addUnit(new GeneralUnit(0.45359237,"lb"));
124 UNITS_ANGLE = new UnitGroup();
125 UNITS_ANGLE.addUnit(new DegreeUnit());
126 UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad",0.01));
128 UNITS_DENSITY_BULK = new UnitGroup();
129 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000,"g/cm\u00b3"));
130 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1,"kg/m\u00b3"));
131 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404,"oz/in\u00b3"));
132 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634,"lb/ft\u00b3"));
134 UNITS_DENSITY_SURFACE = new UnitGroup();
135 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10,"g/cm\u00b2"));
136 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001,"g/m\u00b2"));
137 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1,"kg/m\u00b2"));
138 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487,"oz/in\u00b2"));
139 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727,"oz/ft\u00b2"));
140 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764,"lb/ft\u00b2"));
141 UNITS_DENSITY_SURFACE.setDefaultUnit(1);
143 UNITS_DENSITY_LINE = new UnitGroup();
144 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001,"g/m"));
145 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1,"kg/m"));
146 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465,"oz/ft"));
148 UNITS_FORCE = new UnitGroup();
149 UNITS_FORCE.addUnit(new GeneralUnit(1,"N"));
150 UNITS_FORCE.addUnit(new GeneralUnit(4.44822162,"lbf"));
151 UNITS_FORCE.addUnit(new GeneralUnit(9.80665,"kgf"));
153 UNITS_IMPULSE = new UnitGroup();
154 UNITS_IMPULSE.addUnit(new GeneralUnit(1,"Ns"));
155 UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf\u00b7s"));
157 UNITS_TIME_STEP = new UnitGroup();
158 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001));
159 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("s", 0.01));
160 UNITS_TIME_STEP.setDefaultUnit(1);
162 UNITS_SHORT_TIME = new UnitGroup();
163 UNITS_SHORT_TIME.addUnit(new GeneralUnit(1,"s"));
165 UNITS_FLIGHT_TIME = new UnitGroup();
166 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1,"s"));
167 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60,"min"));
169 UNITS_ROLL = new UnitGroup();
170 UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s"));
171 UNITS_ROLL.addUnit(new GeneralUnit(2*Math.PI, "r/s"));
172 UNITS_ROLL.addUnit(new GeneralUnit(2*Math.PI/60, "rpm"));
173 UNITS_ROLL.setDefaultUnit(1);
175 UNITS_TEMPERATURE = new UnitGroup();
176 UNITS_TEMPERATURE.addUnit(new FixedPrecisionUnit("K", 1));
177 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, "\u00b0C"));
178 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0/9.0, 459.67, "\u00b0F"));
179 UNITS_TEMPERATURE.setDefaultUnit(1);
181 UNITS_PRESSURE = new UnitGroup();
182 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("mbar", 1, 1.0e2));
183 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("bar", 0.001, 1.0e5));
184 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("atm", 0.001, 1.01325e5));
185 UNITS_PRESSURE.addUnit(new GeneralUnit(101325.0/760.0, "mmHg"));
186 UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg"));
187 UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi"));
188 UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa"));
190 UNITS_RELATIVE = new UnitGroup();
191 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("\u200b", 0.01));
192 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01));
193 UNITS_RELATIVE.setDefaultUnit(1);
196 UNITS_ROUGHNESS = new UnitGroup();
197 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, "\u03bcm"));
198 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil"));
201 UNITS_COEFFICIENT = new UnitGroup();
202 UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit("\u200b", 0.01)); // zero-width space
205 HashMap<String,UnitGroup> map = new HashMap<String,UnitGroup>();
206 map.put("NONE", UNITS_NONE);
207 map.put("LENGTH", UNITS_LENGTH);
208 map.put("MOTOR_DIMENSIONS", UNITS_MOTOR_DIMENSIONS);
209 map.put("DISTANCE", UNITS_DISTANCE);
210 map.put("VELOCITY", UNITS_VELOCITY);
211 map.put("ACCELERATION", UNITS_ACCELERATION);
212 map.put("AREA", UNITS_AREA);
213 map.put("STABILITY", UNITS_STABILITY);
214 map.put("MASS", UNITS_MASS);
215 map.put("ANGLE", UNITS_ANGLE);
216 map.put("DENSITY_BULK", UNITS_DENSITY_BULK);
217 map.put("DENSITY_SURFACE", UNITS_DENSITY_SURFACE);
218 map.put("DENSITY_LINE", UNITS_DENSITY_LINE);
219 map.put("FORCE", UNITS_FORCE);
220 map.put("IMPULSE", UNITS_IMPULSE);
221 map.put("TIME_STEP", UNITS_TIME_STEP);
222 map.put("SHORT_TIME", UNITS_SHORT_TIME);
223 map.put("FLIGHT_TIME", UNITS_FLIGHT_TIME);
224 map.put("ROLL", UNITS_ROLL);
225 map.put("TEMPERATURE", UNITS_TEMPERATURE);
226 map.put("PRESSURE", UNITS_PRESSURE);
227 map.put("RELATIVE", UNITS_RELATIVE);
228 map.put("ROUGHNESS", UNITS_ROUGHNESS);
229 map.put("COEFFICIENT", UNITS_COEFFICIENT);
231 UNITS = Collections.unmodifiableMap(map);
234 public static void setDefaultMetricUnits() {
235 UNITS_LENGTH.setDefaultUnit("cm");
236 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("mm");
237 UNITS_DISTANCE.setDefaultUnit("m");
238 UNITS_AREA.setDefaultUnit("cm\u00b2");
239 UNITS_STABILITY.setDefaultUnit("cal");
240 UNITS_VELOCITY.setDefaultUnit("m/s");
241 UNITS_ACCELERATION.setDefaultUnit("m/s\u00b2");
242 UNITS_MASS.setDefaultUnit("g");
243 UNITS_ANGLE.setDefaultUnit(0);
244 UNITS_DENSITY_BULK.setDefaultUnit("g/cm\u00b3");
245 UNITS_DENSITY_SURFACE.setDefaultUnit("g/m\u00b2");
246 UNITS_DENSITY_LINE.setDefaultUnit("g/m");
247 UNITS_FORCE.setDefaultUnit("N");
248 UNITS_IMPULSE.setDefaultUnit("Ns");
249 UNITS_TIME_STEP.setDefaultUnit("s");
250 UNITS_FLIGHT_TIME.setDefaultUnit("s");
251 UNITS_ROLL.setDefaultUnit("r/s");
252 UNITS_TEMPERATURE.setDefaultUnit(1);
253 UNITS_PRESSURE.setDefaultUnit("mbar");
254 UNITS_RELATIVE.setDefaultUnit("%");
255 UNITS_ROUGHNESS.setDefaultUnit("\u03bcm");
258 public static void setDefaultImperialUnits() {
259 UNITS_LENGTH.setDefaultUnit("in");
260 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("in");
261 UNITS_DISTANCE.setDefaultUnit("ft");
262 UNITS_AREA.setDefaultUnit("in\u00b2");
263 UNITS_STABILITY.setDefaultUnit("cal");
264 UNITS_VELOCITY.setDefaultUnit("ft/s");
265 UNITS_ACCELERATION.setDefaultUnit("ft/s\u00b2");
266 UNITS_MASS.setDefaultUnit("oz");
267 UNITS_ANGLE.setDefaultUnit(0);
268 UNITS_DENSITY_BULK.setDefaultUnit("oz/in\u00b3");
269 UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft\u00b2");
270 UNITS_DENSITY_LINE.setDefaultUnit("oz/ft");
271 UNITS_FORCE.setDefaultUnit("N");
272 UNITS_IMPULSE.setDefaultUnit("Ns");
273 UNITS_TIME_STEP.setDefaultUnit("s");
274 UNITS_FLIGHT_TIME.setDefaultUnit("s");
275 UNITS_ROLL.setDefaultUnit("r/s");
276 UNITS_TEMPERATURE.setDefaultUnit(2);
277 UNITS_PRESSURE.setDefaultUnit("mbar");
278 UNITS_RELATIVE.setDefaultUnit("%");
279 UNITS_ROUGHNESS.setDefaultUnit("mil");
284 public static UnitGroup stabilityUnits(Rocket rocket) {
285 return new StabilityUnitGroup(rocket);
289 public static UnitGroup stabilityUnits(Configuration config) {
290 return new StabilityUnitGroup(config);
294 //////////////////////////////////////////////////////
297 private ArrayList<Unit> units = new ArrayList<Unit>();
298 private int defaultUnit = 0;
300 public int getUnitCount() {
304 public Unit getDefaultUnit() {
305 return units.get(defaultUnit);
308 public int getDefaultUnitIndex() {
312 public void setDefaultUnit(int n) {
313 if (n<0 || n>=units.size()) {
314 throw new IllegalArgumentException("index out of range: "+n);
320 * Set the default unit based on the unit name. Does nothing if the name
321 * does not match any of the units.
323 * @param name the unit name (<code>null</code> ok).
324 * @return <code>true</code> if the the default was set,
325 * <code>false</code> if a matching unit was not found.
327 public boolean setDefaultUnit(String name) {
331 for (int i=0; i < units.size(); i++) {
332 if (name.equals(units.get(i).getUnit())) {
341 public Unit getUnit(int n) {
345 public int getUnitIndex(Unit u) {
346 return units.indexOf(u);
349 public void addUnit(Unit u) {
353 public void addUnit(int n, Unit u) {
357 public void removeUnit(int n) {
361 public boolean contains(Unit u) {
362 return units.contains(u);
365 public Unit[] getUnits() {
366 return units.toArray(new Unit[0]);
371 * Return the value formatted by the default unit of this group.
372 * It is the same as calling <code>getDefaultUnit().toString(value)</code>.
374 * @param value the SI value to format.
375 * @return the formatted string.
376 * @see Unit#toString(double)
378 public String toString(double value) {
379 return this.getDefaultUnit().toString(value);
384 * Return the value formatted by the default unit of this group including the unit.
385 * It is the same as calling <code>getDefaultUnit().toStringUnit(value)</code>.
387 * @param value the SI value to format.
388 * @return the formatted string.
389 * @see Unit#toStringUnit(double)
391 public String toStringUnit(double value) {
392 return this.getDefaultUnit().toStringUnit(value);
396 private static final Pattern STRING_PATTERN = Pattern.compile("^\\s*([0-9.,-]+)(.*?)$");
398 * Converts a string into an SI value. If the string has one of the units in this
399 * group appended to it, that unit will be used in conversion. Otherwise the default
400 * unit will be used. If an unknown unit is specified or the value does not parse
401 * with <code>Double.parseDouble</code> then a <code>NumberFormatException</code>
404 * This method is applicable only for simple units without e.g. powers.
406 * @param str the string to parse.
407 * @return the SI value.
408 * @throws NumberFormatException if the string cannot be parsed.
410 public double fromString(String str) {
411 Matcher matcher = STRING_PATTERN.matcher(str);
413 if (!matcher.matches()) {
414 throw new NumberFormatException("string did not match required pattern");
417 double value = Double.parseDouble(matcher.group(1));
418 String unit = matcher.group(2).trim();
420 if (unit.equals("")) {
421 value = this.getDefaultUnit().fromUnit(value);
424 for (i=0; i < units.size(); i++) {
425 Unit u = units.get(i);
426 if (unit.equalsIgnoreCase(u.getUnit())) {
427 value = u.fromUnit(value);
431 if (i >= units.size()) {
432 throw new NumberFormatException("unknown unit "+unit);
440 ///////////////////////////
444 * A private class that switches the CaliberUnit to a rocket-specific CaliberUnit.
445 * All other methods are passed through to UNITS_STABILITY.
447 private static class StabilityUnitGroup extends UnitGroup {
449 private final CaliberUnit caliberUnit;
452 public StabilityUnitGroup(Rocket rocket) {
453 caliberUnit = new CaliberUnit(rocket);
456 public StabilityUnitGroup(Configuration config) {
457 caliberUnit = new CaliberUnit(config);
461 //// Modify CaliberUnit to use local variable
464 public Unit getDefaultUnit() {
465 return getUnit(UNITS_STABILITY.getDefaultUnitIndex());
469 public Unit getUnit(int n) {
470 Unit u = UNITS_STABILITY.getUnit(n);
471 if (u instanceof CaliberUnit) {
478 public int getUnitIndex(Unit u) {
479 if (u instanceof CaliberUnit) {
480 for (int i=0; i < UNITS_STABILITY.getUnitCount(); i++) {
481 if (UNITS_STABILITY.getUnit(i) instanceof CaliberUnit)
485 return UNITS_STABILITY.getUnitIndex(u);
490 //// Pass on to UNITS_STABILITY
493 public int getDefaultUnitIndex() {
494 return UNITS_STABILITY.getDefaultUnitIndex();
498 public void setDefaultUnit(int n) {
499 UNITS_STABILITY.setDefaultUnit(n);
503 public int getUnitCount() {
504 return UNITS_STABILITY.getUnitCount();
508 //// Unsupported methods
511 public void addUnit(int n, Unit u) {
512 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");
516 public void addUnit(Unit u) {
517 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");
521 public void removeUnit(int n) {
522 throw new UnsupportedOperationException("StabilityUnitGroup must not be modified");