create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / unit / UnitGroup.java
1 package net.sf.openrocket.unit;
2
3 import static net.sf.openrocket.util.Chars.*;
4 import static net.sf.openrocket.util.MathUtil.pow2;
5
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.Map;
10 import java.util.regex.Matcher;
11 import java.util.regex.Pattern;
12
13 import net.sf.openrocket.rocketcomponent.Configuration;
14 import net.sf.openrocket.rocketcomponent.Rocket;
15
16
17 /**
18  * A group of units (eg. length, mass etc.).  Contains a list of different units of a same
19  * quantity.
20  * 
21  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
22  */
23
24 public class UnitGroup {
25         
26         public static final UnitGroup UNITS_NONE;
27         
28         public static final UnitGroup UNITS_MOTOR_DIMENSIONS;
29         public static final UnitGroup UNITS_LENGTH;
30         public static final UnitGroup UNITS_ALL_LENGTHS;
31         public static final UnitGroup UNITS_DISTANCE;
32         
33         public static final UnitGroup UNITS_AREA;
34         public static final UnitGroup UNITS_STABILITY;
35         /**
36          * This unit group contains only the caliber unit that never scales the originating "SI" value.
37          * It can be used in cases where the originating value is already in calibers to obtains the correct unit.
38          */
39         public static final UnitGroup UNITS_STABILITY_CALIBERS;
40         public static final UnitGroup UNITS_VELOCITY;
41         public static final UnitGroup UNITS_WINDSPEED;
42         public static final UnitGroup UNITS_ACCELERATION;
43         public static final UnitGroup UNITS_MASS;
44         public static final UnitGroup UNITS_INERTIA;
45         public static final UnitGroup UNITS_ANGLE;
46         public static final UnitGroup UNITS_DENSITY_BULK;
47         public static final UnitGroup UNITS_DENSITY_SURFACE;
48         public static final UnitGroup UNITS_DENSITY_LINE;
49         public static final UnitGroup UNITS_FORCE;
50         public static final UnitGroup UNITS_IMPULSE;
51         
52         /** Time in the order of less than a second (time step etc). */
53         public static final UnitGroup UNITS_TIME_STEP;
54         
55         /** Time in the order of seconds (motor delay etc). */
56         public static final UnitGroup UNITS_SHORT_TIME;
57         
58         /** Time in the order of the flight time of a rocket. */
59         public static final UnitGroup UNITS_FLIGHT_TIME;
60         public static final UnitGroup UNITS_ROLL;
61         public static final UnitGroup UNITS_TEMPERATURE;
62         public static final UnitGroup UNITS_PRESSURE;
63         public static final UnitGroup UNITS_RELATIVE;
64         public static final UnitGroup UNITS_ROUGHNESS;
65         
66         public static final UnitGroup UNITS_COEFFICIENT;
67         public static final UnitGroup UNITS_FREQUENCY;
68         
69         public static final UnitGroup UNITS_ENERGY;
70         public static final UnitGroup UNITS_POWER;
71         public static final UnitGroup UNITS_MOMENTUM;
72         public static final UnitGroup UNITS_VOLTAGE;
73         public static final UnitGroup UNITS_CURRENT;
74         
75         
76         public static final Map<String, UnitGroup> UNITS; // keys such as "LENGTH", "VELOCITY"
77         public static final Map<String, UnitGroup> SIUNITS; // keys such a "m", "m/s"
78         
79         
80         /*
81          * Note:  Units may not use HTML tags.
82          * 
83          * The scaling value "X" is obtained by "one of this unit is X of SI units"
84          * Type into Google for example:  "1 in^2 in m^2"
85          */
86         static {
87                 UNITS_NONE = new UnitGroup();
88                 UNITS_NONE.addUnit(Unit.NOUNIT2);
89                 
90                 UNITS_ENERGY = new UnitGroup();
91                 UNITS_ENERGY.addUnit(new GeneralUnit(1, "J"));
92                 UNITS_ENERGY.addUnit(new GeneralUnit(1e-7, "erg"));
93                 UNITS_ENERGY.addUnit(new GeneralUnit(1.055, "BTU"));
94                 UNITS_ENERGY.addUnit(new GeneralUnit(4.184, "cal"));
95                 UNITS_ENERGY.addUnit(new GeneralUnit(1.3558179483314, "ft"+DOT+"lbf"));
96                 UNITS_ENERGY.setDefaultUnit(0);
97                 
98                 UNITS_POWER = new UnitGroup();
99                 UNITS_POWER.addUnit(new GeneralUnit(1e-3, "mW"));
100                 UNITS_POWER.addUnit(new GeneralUnit(1, "W"));
101                 UNITS_POWER.addUnit(new GeneralUnit(1e3, "kW"));
102                 UNITS_POWER.addUnit(new GeneralUnit(1e-7, "ergs"));
103                 UNITS_POWER.addUnit(new GeneralUnit(745.699872, "hp"));
104                 UNITS_POWER.setDefaultUnit(1);
105                 
106                 UNITS_MOMENTUM = new UnitGroup();
107                 UNITS_MOMENTUM.addUnit(new GeneralUnit(1, "kg"+DOT+"m/s"));
108                 UNITS_MOMENTUM.setDefaultUnit(0);
109                 
110                 UNITS_VOLTAGE = new UnitGroup();
111                 UNITS_VOLTAGE.addUnit(new GeneralUnit(1e-3, "mV"));
112                 UNITS_VOLTAGE.addUnit(new GeneralUnit(1, "V"));
113                 UNITS_VOLTAGE.setDefaultUnit(1);
114                 
115                 UNITS_CURRENT = new UnitGroup();
116                 UNITS_CURRENT.addUnit(new GeneralUnit(1e-3, "mA"));
117                 UNITS_CURRENT.addUnit(new GeneralUnit(1, "A"));
118                 UNITS_CURRENT.setDefaultUnit(1);
119                 
120                 UNITS_LENGTH = new UnitGroup();
121                 UNITS_LENGTH.addUnit(new GeneralUnit(0.001, "mm"));
122                 UNITS_LENGTH.addUnit(new GeneralUnit(0.01, "cm"));
123                 UNITS_LENGTH.addUnit(new GeneralUnit(1, "m"));
124                 UNITS_LENGTH.addUnit(new GeneralUnit(0.0254, "in"));
125                 UNITS_LENGTH.addUnit(new FractionalUnit(0.0254, "in/64", "in", 64, 1d / 16d, 0.5d / 64d));
126                 UNITS_LENGTH.addUnit(new GeneralUnit(0.3048, "ft"));
127                 UNITS_LENGTH.setDefaultUnit(1);
128                 
129                 UNITS_MOTOR_DIMENSIONS = new UnitGroup();
130                 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(1, "m")); // just added
131                 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.001, "mm"));
132                 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.01, "cm"));
133                 UNITS_MOTOR_DIMENSIONS.addUnit(new GeneralUnit(0.0254, "in"));
134                 UNITS_MOTOR_DIMENSIONS.setDefaultUnit(0);
135                 
136                 UNITS_DISTANCE = new UnitGroup();
137                 UNITS_DISTANCE.addUnit(new GeneralUnit(1, "m"));
138                 UNITS_DISTANCE.addUnit(new GeneralUnit(1000, "km"));
139                 UNITS_DISTANCE.addUnit(new GeneralUnit(0.3048, "ft"));
140                 UNITS_DISTANCE.addUnit(new GeneralUnit(0.9144, "yd"));
141                 UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344, "mi"));
142                 UNITS_DISTANCE.addUnit(new GeneralUnit(1852, "nmi"));
143                 
144                 UNITS_ALL_LENGTHS = new UnitGroup();
145                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.001, "mm"));
146                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.01, "cm"));
147                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1, "m"));
148                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1000, "km"));
149                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.0254, "in"));
150                 UNITS_ALL_LENGTHS.addUnit(new FractionalUnit(0.0254, "in/64", "in", 64, 1d / 16d, 0.5d / 64d));
151                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.3048, "ft"));
152                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(0.9144, "yd"));
153                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1609.344, "mi"));
154                 UNITS_ALL_LENGTHS.addUnit(new GeneralUnit(1852, "nmi"));
155                 UNITS_ALL_LENGTHS.setDefaultUnit(2);
156                 
157                 UNITS_AREA = new UnitGroup();
158                 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001), "mm" + SQUARED));
159                 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01), "cm" + SQUARED));
160                 UNITS_AREA.addUnit(new GeneralUnit(1, "m" + SQUARED));
161                 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254), "in" + SQUARED));
162                 UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048), "ft" + SQUARED));
163                 UNITS_AREA.setDefaultUnit(1);
164                 
165                 
166                 UNITS_STABILITY = new UnitGroup();
167                 UNITS_STABILITY.addUnit(new GeneralUnit(1, "m"));
168                 UNITS_STABILITY.addUnit(new GeneralUnit(0.001, "mm"));
169                 UNITS_STABILITY.addUnit(new GeneralUnit(0.01, "cm"));
170                 UNITS_STABILITY.addUnit(new GeneralUnit(0.0254, "in"));
171                 UNITS_STABILITY.addUnit(new CaliberUnit((Rocket) null));
172                 UNITS_STABILITY.setDefaultUnit(3);
173                 
174                 UNITS_STABILITY_CALIBERS = new UnitGroup();
175                 UNITS_STABILITY_CALIBERS.addUnit(new GeneralUnit(1, "cal"));
176                 
177                 
178                 UNITS_VELOCITY = new UnitGroup();
179                 UNITS_VELOCITY.addUnit(new GeneralUnit(1, "m/s"));
180                 UNITS_VELOCITY.addUnit(new GeneralUnit(1 / 3.6, "km/h"));
181                 UNITS_VELOCITY.addUnit(new GeneralUnit(0.3048, "ft/s"));
182                 UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph"));
183                 
184                 UNITS_WINDSPEED = new UnitGroup();
185                 UNITS_WINDSPEED.addUnit(new GeneralUnit(1, "m/s"));
186                 UNITS_WINDSPEED.addUnit(new GeneralUnit(1 / 3.6, "km/h"));
187                 UNITS_WINDSPEED.addUnit(new GeneralUnit(0.3048, "ft/s"));
188                 UNITS_WINDSPEED.addUnit(new GeneralUnit(0.44704, "mph"));
189                 
190                 UNITS_ACCELERATION = new UnitGroup();
191                 UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED));
192                 UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED));
193                 UNITS_ACCELERATION.addUnit(new GeneralUnit(9.80665, "G"));
194                 
195                 UNITS_MASS = new UnitGroup();
196                 UNITS_MASS.addUnit(new GeneralUnit(0.001, "g"));
197                 UNITS_MASS.addUnit(new GeneralUnit(1, "kg"));
198                 UNITS_MASS.addUnit(new GeneralUnit(0.0283495231, "oz"));
199                 UNITS_MASS.addUnit(new GeneralUnit(0.45359237, "lb"));
200                 
201                 UNITS_INERTIA = new UnitGroup();
202                 UNITS_INERTIA.addUnit(new GeneralUnit(0.0001, "kg" + DOT + "cm" + SQUARED));
203                 UNITS_INERTIA.addUnit(new GeneralUnit(1, "kg" + DOT + "m" + SQUARED));
204                 UNITS_INERTIA.addUnit(new GeneralUnit(1.82899783e-5, "oz" + DOT + "in" + SQUARED));
205                 UNITS_INERTIA.addUnit(new GeneralUnit(0.000292639653, "lb" + DOT + "in" + SQUARED));
206                 UNITS_INERTIA.addUnit(new GeneralUnit(0.0421401101, "lb" + DOT + "ft" + SQUARED));
207                 UNITS_INERTIA.addUnit(new GeneralUnit(1.35581795, "lbf" + DOT + "ft" + DOT + "s" + SQUARED));
208                 UNITS_INERTIA.setDefaultUnit(1);
209                 
210                 UNITS_ANGLE = new UnitGroup();
211                 UNITS_ANGLE.addUnit(new DegreeUnit());
212                 UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad", 0.01));
213                 UNITS_ANGLE.addUnit(new GeneralUnit(1.0 / 3437.74677078, "arcmin"));
214                 
215                 UNITS_DENSITY_BULK = new UnitGroup();
216                 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000, "g/cm" + CUBED));
217                 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000, "kg/dm" + CUBED));
218                 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1, "kg/m" + CUBED));
219                 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404, "oz/in" + CUBED));
220                 UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634, "lb/ft" + CUBED));
221                 
222                 UNITS_DENSITY_SURFACE = new UnitGroup();
223                 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10, "g/cm" + SQUARED));
224                 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001, "g/m" + SQUARED));
225                 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1, "kg/m" + SQUARED));
226                 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487, "oz/in" + SQUARED));
227                 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727, "oz/ft" + SQUARED));
228                 UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764, "lb/ft" + SQUARED));
229                 UNITS_DENSITY_SURFACE.setDefaultUnit(1);
230                 
231                 UNITS_DENSITY_LINE = new UnitGroup();
232                 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.001, "g/m"));
233                 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(1, "kg/m"));
234                 UNITS_DENSITY_LINE.addUnit(new GeneralUnit(0.0930102465, "oz/ft"));
235                 
236                 UNITS_FORCE = new UnitGroup();
237                 UNITS_FORCE.addUnit(new GeneralUnit(1, "N"));
238                 UNITS_FORCE.addUnit(new GeneralUnit(4.44822162, "lbf"));
239                 UNITS_FORCE.addUnit(new GeneralUnit(9.80665, "kgf"));
240                 
241                 UNITS_IMPULSE = new UnitGroup();
242                 UNITS_IMPULSE.addUnit(new GeneralUnit(1, "Ns"));
243                 UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf" + DOT + "s"));
244                 
245                 UNITS_TIME_STEP = new UnitGroup();
246                 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001));
247                 UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("s", 0.01));
248                 UNITS_TIME_STEP.setDefaultUnit(1);
249                 
250                 UNITS_SHORT_TIME = new UnitGroup();
251                 UNITS_SHORT_TIME.addUnit(new GeneralUnit(1, "s"));
252                 
253                 UNITS_FLIGHT_TIME = new UnitGroup();
254                 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(1, "s"));
255                 UNITS_FLIGHT_TIME.addUnit(new GeneralUnit(60, "min"));
256                 
257                 UNITS_ROLL = new UnitGroup();
258                 UNITS_ROLL.addUnit(new GeneralUnit(1, "rad/s"));
259                 UNITS_ROLL.addUnit(new GeneralUnit(2 * Math.PI, "r/s"));
260                 UNITS_ROLL.addUnit(new GeneralUnit(2 * Math.PI / 60, "rpm"));
261                 UNITS_ROLL.setDefaultUnit(1);
262                 
263                 UNITS_TEMPERATURE = new UnitGroup();
264                 UNITS_TEMPERATURE.addUnit(new FixedPrecisionUnit("K", 1));
265                 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, DEGREE + "C"));
266                 UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0 / 9.0, 459.67, DEGREE + "F"));
267                 UNITS_TEMPERATURE.setDefaultUnit(1);
268                 
269                 UNITS_PRESSURE = new UnitGroup();
270                 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("mbar", 1, 1.0e2));
271                 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("bar", 0.001, 1.0e5));
272                 UNITS_PRESSURE.addUnit(new FixedPrecisionUnit("atm", 0.001, 1.01325e5));
273                 UNITS_PRESSURE.addUnit(new GeneralUnit(101325.0 / 760.0, "mmHg"));
274                 UNITS_PRESSURE.addUnit(new GeneralUnit(3386.389, "inHg"));
275                 UNITS_PRESSURE.addUnit(new GeneralUnit(6894.75729, "psi"));
276                 UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa"));
277                 
278                 UNITS_RELATIVE = new UnitGroup();
279                 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0));
280                 UNITS_RELATIVE.addUnit(new GeneralUnit(0.01, "%"));
281                 UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001));
282                 //              UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01, 1.0));
283                 //              UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01));
284                 //              UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("" + PERMILLE, 1, 0.001));
285                 UNITS_RELATIVE.setDefaultUnit(1);
286                 
287                 
288                 UNITS_ROUGHNESS = new UnitGroup();
289                 UNITS_ROUGHNESS.addUnit(new GeneralUnit(1, "m")); // just added
290                 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO + "m"));
291                 UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil"));
292                 
293                 
294                 UNITS_COEFFICIENT = new UnitGroup();
295                 UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit("" + ZWSP, 0.01)); // zero-width space
296                 
297                 
298                 // This is not used by OpenRocket, and not extensively tested:
299                 UNITS_FREQUENCY = new UnitGroup();
300                 //              UNITS_FREQUENCY.addUnit(new GeneralUnit(1, "s"));
301                 //              UNITS_FREQUENCY.addUnit(new GeneralUnit(0.001, "ms"));
302                 //              UNITS_FREQUENCY.addUnit(new GeneralUnit(0.000001, MICRO + "s"));
303                 UNITS_FREQUENCY.addUnit(new FrequencyUnit(.001, "mHz"));
304                 UNITS_FREQUENCY.addUnit(new FrequencyUnit(1, "Hz"));
305                 UNITS_FREQUENCY.addUnit(new FrequencyUnit(1000, "kHz"));
306                 UNITS_FREQUENCY.setDefaultUnit(1);
307                 
308                 
309                 HashMap<String, UnitGroup> map = new HashMap<String, UnitGroup>();
310                 map.put("NONE", UNITS_NONE);
311                 map.put("LENGTH", UNITS_LENGTH);
312                 map.put("ALL_LENGTHS", UNITS_ALL_LENGTHS);
313                 map.put("MOTOR_DIMENSIONS", UNITS_MOTOR_DIMENSIONS);
314                 map.put("DISTANCE", UNITS_DISTANCE);
315                 map.put("VELOCITY", UNITS_VELOCITY);
316                 map.put("ACCELERATION", UNITS_ACCELERATION);
317                 map.put("AREA", UNITS_AREA);
318                 map.put("STABILITY", UNITS_STABILITY);
319                 map.put("MASS", UNITS_MASS);
320                 map.put("INERTIA", UNITS_INERTIA);
321                 map.put("ANGLE", UNITS_ANGLE);
322                 map.put("DENSITY_BULK", UNITS_DENSITY_BULK);
323                 map.put("DENSITY_SURFACE", UNITS_DENSITY_SURFACE);
324                 map.put("DENSITY_LINE", UNITS_DENSITY_LINE);
325                 map.put("FORCE", UNITS_FORCE);
326                 map.put("IMPULSE", UNITS_IMPULSE);
327                 map.put("TIME_STEP", UNITS_TIME_STEP);
328                 map.put("SHORT_TIME", UNITS_SHORT_TIME);
329                 map.put("FLIGHT_TIME", UNITS_FLIGHT_TIME);
330                 map.put("ROLL", UNITS_ROLL);
331                 map.put("TEMPERATURE", UNITS_TEMPERATURE);
332                 map.put("PRESSURE", UNITS_PRESSURE);
333                 map.put("RELATIVE", UNITS_RELATIVE);
334                 map.put("ROUGHNESS", UNITS_ROUGHNESS);
335                 map.put("COEFFICIENT", UNITS_COEFFICIENT);
336                 map.put("VOLTAGE", UNITS_VOLTAGE);
337                 map.put("CURRENT", UNITS_CURRENT);
338                 map.put("ENERGY", UNITS_ENERGY);
339                 map.put("POWER", UNITS_POWER);
340                 map.put("MOMENTUM", UNITS_MOMENTUM);
341                 map.put("FREQUENCY", UNITS_FREQUENCY);
342                 
343                 UNITS = Collections.unmodifiableMap(map);
344                 
345                 HashMap<String, UnitGroup> simap = new HashMap<String, UnitGroup>();
346                 simap.put("m", UNITS_ALL_LENGTHS);
347                 simap.put("m^2", UNITS_AREA);
348                 simap.put("m/s", UNITS_VELOCITY);
349                 simap.put("m/s^2", UNITS_ACCELERATION); 
350                 simap.put("kg", UNITS_MASS);
351                 simap.put("kg m^2", UNITS_INERTIA);
352                 simap.put("kg/m^3", UNITS_DENSITY_BULK);
353                 simap.put("N", UNITS_FORCE);
354                 simap.put("Ns", UNITS_IMPULSE);
355                 simap.put("s", UNITS_FLIGHT_TIME);
356                 simap.put("Pa", UNITS_PRESSURE);
357                 simap.put("V", UNITS_VOLTAGE);
358                 simap.put("A", UNITS_CURRENT);
359                 simap.put("J", UNITS_ENERGY);
360                 simap.put("W", UNITS_POWER);
361                 simap.put("kg m/s", UNITS_MOMENTUM);
362                 simap.put("Hz", UNITS_FREQUENCY);
363                 simap.put("K", UNITS_TEMPERATURE);
364                 
365                 SIUNITS = Collections.unmodifiableMap(simap);
366         }
367         
368         public static void setDefaultMetricUnits() {
369                 UNITS_LENGTH.setDefaultUnit("cm");
370                 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("mm");
371                 UNITS_DISTANCE.setDefaultUnit("m");
372                 UNITS_AREA.setDefaultUnit("cm" + SQUARED);
373                 UNITS_STABILITY.setDefaultUnit("cal");
374                 UNITS_VELOCITY.setDefaultUnit("m/s");
375                 UNITS_ACCELERATION.setDefaultUnit("m/s" + SQUARED);
376                 UNITS_MASS.setDefaultUnit("g");
377                 UNITS_INERTIA.setDefaultUnit("kg" + DOT + "m" + SQUARED);
378                 UNITS_ANGLE.setDefaultUnit("" + DEGREE);
379                 UNITS_DENSITY_BULK.setDefaultUnit("g/cm" + CUBED);
380                 UNITS_DENSITY_SURFACE.setDefaultUnit("g/m" + SQUARED);
381                 UNITS_DENSITY_LINE.setDefaultUnit("g/m");
382                 UNITS_FORCE.setDefaultUnit("N");
383                 UNITS_IMPULSE.setDefaultUnit("Ns");
384                 UNITS_TIME_STEP.setDefaultUnit("s");
385                 UNITS_FLIGHT_TIME.setDefaultUnit("s");
386                 UNITS_ROLL.setDefaultUnit("r/s");
387                 UNITS_TEMPERATURE.setDefaultUnit(DEGREE + "C");
388                 UNITS_WINDSPEED.setDefaultUnit("m/s");
389                 UNITS_PRESSURE.setDefaultUnit("mbar");
390                 UNITS_RELATIVE.setDefaultUnit("%");
391                 UNITS_ROUGHNESS.setDefaultUnit(MICRO + "m");
392         }
393         
394         public static void setDefaultImperialUnits() {
395                 UNITS_LENGTH.setDefaultUnit("in");
396                 UNITS_MOTOR_DIMENSIONS.setDefaultUnit("in");
397                 UNITS_DISTANCE.setDefaultUnit("ft");
398                 UNITS_AREA.setDefaultUnit("in" + SQUARED);
399                 UNITS_STABILITY.setDefaultUnit("cal");
400                 UNITS_VELOCITY.setDefaultUnit("ft/s");
401                 UNITS_ACCELERATION.setDefaultUnit("ft/s" + SQUARED);
402                 UNITS_MASS.setDefaultUnit("oz");
403                 UNITS_INERTIA.setDefaultUnit("lb" + DOT + "ft" + SQUARED);
404                 UNITS_ANGLE.setDefaultUnit("" + DEGREE);
405                 UNITS_DENSITY_BULK.setDefaultUnit("oz/in" + CUBED);
406                 UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft" + SQUARED);
407                 UNITS_DENSITY_LINE.setDefaultUnit("oz/ft");
408                 UNITS_FORCE.setDefaultUnit("N");
409                 UNITS_IMPULSE.setDefaultUnit("Ns");
410                 UNITS_TIME_STEP.setDefaultUnit("s");
411                 UNITS_FLIGHT_TIME.setDefaultUnit("s");
412                 UNITS_ROLL.setDefaultUnit("r/s");
413                 UNITS_TEMPERATURE.setDefaultUnit(DEGREE + "F");
414                 UNITS_WINDSPEED.setDefaultUnit("mph");
415                 UNITS_PRESSURE.setDefaultUnit("mbar");
416                 UNITS_RELATIVE.setDefaultUnit("%");
417                 UNITS_ROUGHNESS.setDefaultUnit("mil");
418         }
419         
420         
421         /**
422          * Return a UnitGroup for stability units based on the rocket.
423          * 
424          * @param rocket        the rocket from which to calculate the caliber
425          * @return                      the unit group
426          */
427         public static UnitGroup stabilityUnits(Rocket rocket) {
428                 return new StabilityUnitGroup(rocket);
429         }
430         
431         
432         /**
433          * Return a UnitGroup for stability units based on the rocket configuration.
434          * 
435          * @param config        the rocket configuration from which to calculate the caliber
436          * @return                      the unit group
437          */
438         public static UnitGroup stabilityUnits(Configuration config) {
439                 return new StabilityUnitGroup(config);
440         }
441         
442         
443         /**
444          * Return a UnitGroup for stability units based on a constant caliber.
445          * 
446          * @param reference     the constant reference length
447          * @return                      the unit group
448          */
449         public static UnitGroup stabilityUnits(double reference) {
450                 return new StabilityUnitGroup(reference);
451         }
452         
453         
454         //////////////////////////////////////////////////////
455         
456         
457         protected ArrayList<Unit> units = new ArrayList<Unit>();
458         protected int defaultUnit = 0;
459         
460         public int getUnitCount() {
461                 return units.size();
462         }
463         
464         public Unit getDefaultUnit() {
465                 return units.get(defaultUnit);
466         }
467         
468         public int getDefaultUnitIndex() {
469                 return defaultUnit;
470         }
471         
472         public void setDefaultUnit(int n) {
473                 if (n < 0 || n >= units.size()) {
474                         throw new IllegalArgumentException("index out of range: " + n);
475                 }
476                 defaultUnit = n;
477         }
478         
479         public Unit getSIUnit(){
480                 for (Unit u : units){
481                         if (u.multiplier == 1){
482                                 return u;
483                         }
484                 }
485                 return UNITS_NONE.getDefaultUnit();
486         }
487         
488         /**
489          * Find a unit by approximate unit name.  Only letters and (ordinary) numbers are
490          * considered in the matching.  This method is mainly means for testing, allowing
491          * a simple means to obtain a particular unit.
492          * 
493          * @param str   the unit name.
494          * @return              the corresponding unit, or <code>null</code> if not found.
495          */
496         public Unit findApproximate(String str) {
497                 str = str.replaceAll("\\W", "").trim();
498                 for (Unit u : units) {
499                         String name = u.getUnit().replaceAll("\\W", "").trim();
500                         if (str.equalsIgnoreCase(name))
501                                 return u;
502                 }
503                 return null;
504         }
505         
506         /**
507          * Set the default unit based on the unit name.  Throws an exception if a
508          * unit with the provided name is not available.
509          * 
510          * @param   name        the unit name.
511          * @throws  IllegalArgumentException    if the corresponding unit is not found in the group.
512          */
513         public void setDefaultUnit(String name) throws IllegalArgumentException {
514                 for (int i = 0; i < units.size(); i++) {
515                         if (units.get(i).getUnit().equals(name)) {
516                                 setDefaultUnit(i);
517                                 return;
518                         }
519                 }
520                 throw new IllegalArgumentException("name=" + name);
521         }
522         
523         public Unit getUnit(String name) throws IllegalArgumentException {
524                 for (int i = 0; i < units.size(); i++) {
525                         if (units.get(i).getUnit().equals(name)) {
526                                 return units.get(i);
527                         }
528                 }
529                 throw new IllegalArgumentException("name=" + name);
530         }
531         
532         public Unit getUnit(int n) {
533                 return units.get(n);
534         }
535         
536         public int getUnitIndex(Unit u) {
537                 return units.indexOf(u);
538         }
539         
540         public void addUnit(Unit u) {
541                 units.add(u);
542         }
543         
544         public boolean contains(Unit u) {
545                 return units.contains(u);
546         }
547         
548         public Unit[] getUnits() {
549                 return units.toArray(new Unit[0]);
550         }
551         
552         /**
553          * Return the value in SI units from the default unit of this group.
554          * It is the same as calling <code>getDefaultUnit().fromUnit(value)</code>
555          * 
556          * @param value the default unit value to convert
557          * @return the value in SI units.
558          * @see Unit#fromUnit(double)
559          */
560         public double fromUnit( double value ) {
561                 return this.getDefaultUnit().fromUnit(value);
562         }
563         
564         /**
565          * Return the value formatted by the default unit of this group.
566          * It is the same as calling <code>getDefaultUnit().toString(value)</code>.
567          * 
568          * @param value         the SI value to format.
569          * @return                      the formatted string.
570          * @see                         Unit#toString(double)
571          */
572         public String toString(double value) {
573                 return this.getDefaultUnit().toString(value);
574         }
575         
576         
577         /**
578          * Return the value formatted by the default unit of this group including the unit.
579          * It is the same as calling <code>getDefaultUnit().toStringUnit(value)</code>.
580          * 
581          * @param value         the SI value to format.
582          * @return                      the formatted string.
583          * @see                         Unit#toStringUnit(double)
584          */
585         public String toStringUnit(double value) {
586                 return this.getDefaultUnit().toStringUnit(value);
587         }
588         
589         
590         
591         
592         
593         /**
594          * Creates a new Value object with the specified value and the default unit of this group.
595          * 
596          * @param value the value to set.
597          * @return              a new Value object.
598          */
599         public Value toValue(double value) {
600                 return this.getDefaultUnit().toValue(value);
601         }
602         
603         @Override
604         public String toString(){
605                 return this.getClass().getSimpleName()+":"+this.getSIUnit().toString();
606         }
607         
608         @Override
609         public boolean equals(Object o){
610                 UnitGroup u = (UnitGroup) o;
611                 int size = units.size();
612                 if (size != u.units.size()){
613                         return false;
614                 }
615                 
616                 for (int i=0; i<size; i++){
617                         if ( !units.get(i).equals(u.units.get(i)) ){
618                                 return false;
619                         }
620                 }
621                 
622                 return true;
623                         
624         }
625         
626         
627         private static final Pattern STRING_PATTERN = Pattern.compile("^\\s*([0-9.,-]+)(.*?)$");
628         
629         /**
630          * Converts a string into an SI value.  If the string has one of the units in this
631          * group appended to it, that unit will be used in conversion.  Otherwise the default
632          * unit will be used.  If an unknown unit is specified or the value does not parse
633          * with <code>Double.parseDouble</code> then a <code>NumberFormatException</code> 
634          * is thrown.
635          * <p>
636          * This method is applicable only for simple units without e.g. powers.
637          * 
638          * @param str   the string to parse.
639          * @return              the SI value.
640          * @throws NumberFormatException   if the string cannot be parsed.
641          */
642         public double fromString(String str) {
643                 Matcher matcher = STRING_PATTERN.matcher(str);
644                 
645                 if (!matcher.matches()) {
646                         throw new NumberFormatException("string did not match required pattern");
647                 }
648                 
649                 double value = Double.parseDouble(matcher.group(1));
650                 String unit = matcher.group(2).trim();
651                 
652                 if (unit.equals("")) {
653                         value = this.getDefaultUnit().fromUnit(value);
654                 } else {
655                         int i;
656                         for (i = 0; i < units.size(); i++) {
657                                 Unit u = units.get(i);
658                                 if (unit.equalsIgnoreCase(u.getUnit())) {
659                                         value = u.fromUnit(value);
660                                         break;
661                                 }
662                         }
663                         if (i >= units.size()) {
664                                 throw new NumberFormatException("unknown unit " + unit);
665                         }
666                 }
667                 
668                 return value;
669         }
670         
671         
672         ///////////////////////////
673         
674         
675         @Override
676         public int hashCode() {
677                 int code = 0;
678                 for (Unit u : units){
679                         code = code + u.hashCode();
680                 }
681                 return code;
682         }
683
684         /**
685          * A private class that switches the CaliberUnit to a rocket-specific CaliberUnit.
686          * All other methods are passed through to UNITS_STABILITY.
687          */
688         private static class StabilityUnitGroup extends UnitGroup {
689                 
690                 public StabilityUnitGroup(double ref) {
691                         this(new CaliberUnit(ref));
692                 }
693                 
694                 public StabilityUnitGroup(Rocket rocket) {
695                         this(new CaliberUnit(rocket));
696                 }
697                 
698                 public StabilityUnitGroup(Configuration config) {
699                         this(new CaliberUnit(config));
700                 }
701                 
702                 private StabilityUnitGroup(CaliberUnit caliberUnit) {
703                         this.units.addAll(UnitGroup.UNITS_STABILITY.units);
704                         this.defaultUnit = UnitGroup.UNITS_STABILITY.defaultUnit;
705                         for (int i = 0; i < units.size(); i++) {
706                                 if (units.get(i) instanceof CaliberUnit) {
707                                         units.set(i, caliberUnit);
708                                 }
709                         }
710                 }
711                 
712                 
713                 @Override
714                 public void setDefaultUnit(int n) {
715                         super.setDefaultUnit(n);
716                         UNITS_STABILITY.setDefaultUnit(n);
717                 }
718         }
719 }