1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.utils.units;
18
19 import java.io.Serializable;
20 import java.util.List;
21
22 import org.hipparchus.fraction.Fraction;
23 import org.hipparchus.util.FastMath;
24 import org.hipparchus.util.Precision;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitMessages;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class Unit implements Serializable {
43
44
45 public static final Unit NONE = new Unit("n/a", 1.0, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO);
46
47
48 public static final Unit ONE = new Unit("1", 1.0, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO);
49
50
51 public static final Unit PERCENT = new Unit("%", 1.0e-2, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO);
52
53
54 public static final Unit SECOND = new Unit("s", 1.0, Fraction.ZERO, Fraction.ZERO, Fraction.ONE, Fraction.ZERO, Fraction.ZERO);
55
56
57 public static final Unit MINUTE = SECOND.scale("min", 60.0);
58
59
60 public static final Unit HOUR = MINUTE.scale("h", 60);
61
62
63 public static final Unit DAY = HOUR.scale("d", 24.0);
64
65
66
67
68 public static final Unit YEAR = DAY.scale("a", 365.25);
69
70
71 public static final Unit HERTZ = SECOND.power("Hz", Fraction.MINUS_ONE);
72
73
74 public static final Unit METRE = new Unit("m", 1.0, Fraction.ZERO, Fraction.ONE, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO);
75
76
77 public static final Unit KILOMETRE = METRE.scale("km", 1000.0);
78
79
80 public static final Unit KILOGRAM = new Unit("kg", 1.0, Fraction.ONE, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO);
81
82
83 public static final Unit GRAM = KILOGRAM.scale("g", 1.0e-3);
84
85
86 public static final Unit AMPERE = new Unit("A", 1.0, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ONE, Fraction.ZERO);
87
88
89 public static final Unit RADIAN = new Unit("rad", 1.0, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ONE);
90
91
92 public static final Unit DEGREE = RADIAN.scale("°", FastMath.toRadians(1.0));
93
94
95 public static final Unit ARC_MINUTE = DEGREE.scale("′", 1.0 / 60.0);
96
97
98 public static final Unit ARC_SECOND = ARC_MINUTE.scale("″", 1.0 / 60.0);
99
100
101 public static final Unit REVOLUTION = RADIAN.scale("rev", 2.0 * FastMath.PI);
102
103
104 public static final Unit NEWTON = KILOGRAM.multiply(null, METRE).divide("N", SECOND.power(null, Fraction.TWO));
105
106
107 public static final Unit PASCAL = NEWTON.divide("Pa", METRE.power(null, Fraction.TWO));
108
109
110 public static final Unit BAR = PASCAL.scale("bar", 100000.0);
111
112
113 public static final Unit JOULE = NEWTON.multiply("J", METRE);
114
115
116 public static final Unit WATT = JOULE.divide("W", SECOND);
117
118
119 public static final Unit COULOMB = SECOND.multiply("C", AMPERE);
120
121
122 public static final Unit VOLT = WATT.divide("V", AMPERE);
123
124
125 public static final Unit OHM = VOLT.divide("Ω", AMPERE);
126
127
128 public static final Unit TESLA = VOLT.multiply(null, SECOND).divide("T", METRE.power(null, Fraction.TWO));
129
130
131 public static final Unit SOLAR_FLUX_UNIT = WATT.divide(null, METRE.power(null, Fraction.TWO).multiply(null, HERTZ)).scale("SFU", 1.0e-22);
132
133
134 public static final Unit TOTAL_ELECTRON_CONTENT_UNIT = METRE.power(null, new Fraction(-2)).scale("TECU", 1.0e+16);
135
136
137 public static final Unit EARTH_RADII = new Unit("ER", 1.0, Fraction.ZERO, Fraction.ZERO, Fraction.ZERO, Fraction.ONE, Fraction.ZERO);
138
139
140 private static final long serialVersionUID = 20210402L;
141
142
143 private final String name;
144
145
146 private final double scale;
147
148
149 private final Fraction mass;
150
151
152 private final Fraction length;
153
154
155 private final Fraction time;
156
157
158 private final Fraction current;
159
160
161 private final Fraction angle;
162
163
164
165
166
167
168
169
170
171
172 public Unit(final String name, final double scale,
173 final Fraction mass, final Fraction length,
174 final Fraction time, final Fraction current,
175 final Fraction angle) {
176 this.name = name;
177 this.scale = scale;
178 this.mass = mass;
179 this.length = length;
180 this.time = time;
181 this.current = current;
182 this.angle = angle;
183 }
184
185
186
187
188 public String getName() {
189 return name;
190 }
191
192
193
194
195 public double getScale() {
196 return scale;
197 }
198
199
200
201
202 public Fraction getMass() {
203 return mass;
204 }
205
206
207
208
209 public Fraction getLength() {
210 return length;
211 }
212
213
214
215
216 public Fraction getTime() {
217 return time;
218 }
219
220
221
222
223 public Fraction getCurrent() {
224 return current;
225 }
226
227
228
229
230 public Fraction getAngle() {
231 return angle;
232 }
233
234
235
236
237
238 public boolean sameDimension(final Unit other) {
239 return time.equals(other.time) && length.equals(other.length) &&
240 mass.equals(other.mass) && current.equals(other.current) &&
241 angle.equals(other.angle);
242 }
243
244
245
246
247 public Unit sameDimensionSI() {
248 final StringBuilder builder = new StringBuilder();
249 append(builder, KILOGRAM.name, mass);
250 append(builder, METRE.name, length);
251 append(builder, SECOND.name, time);
252 append(builder, AMPERE.name, current);
253 append(builder, RADIAN.name, angle);
254 if (builder.length() == 0) {
255 builder.append('1');
256 }
257 return new Unit(builder.toString(), 1.0, mass, length, time, current, angle);
258 }
259
260
261
262
263
264
265
266
267
268 public static void ensureCompatible(final String description, final List<Unit> reference,
269 final boolean allowScaleDifferences, final List<Unit> units) {
270 if (units.size() != reference.size()) {
271 throw new OrekitException(OrekitMessages.WRONG_NB_COMPONENTS,
272 description, reference.size(), units.size());
273 }
274 for (int i = 0; i < reference.size(); ++i) {
275 if (!reference.get(i).sameDimension(units.get(i))) {
276 throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS,
277 reference.get(i).getName(),
278 units.get(i).getName());
279 }
280 if (!(allowScaleDifferences ||
281 Precision.equals(reference.get(i).getScale(), units.get(i).getScale(), 1))) {
282 throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS,
283 reference.get(i).getName(),
284 units.get(i).getName());
285 }
286 }
287 }
288
289
290
291
292
293
294 private void append(final StringBuilder builder, final String dim, final Fraction exp) {
295 if (!exp.isZero()) {
296 if (builder.length() > 0) {
297 builder.append('.');
298 }
299 builder.append(dim);
300 if (exp.getDenominator() == 1) {
301 if (exp.getNumerator() != 1) {
302 builder.append(Integer.toString(exp.getNumerator()).
303 replace('-', '⁻').
304 replace('0', '⁰').
305 replace('1', '¹').
306 replace('2', '²').
307 replace('3', '³').
308 replace('4', '⁴').
309 replace('5', '⁵').
310 replace('6', '⁶').
311 replace('7', '⁷').
312 replace('8', '⁸').
313 replace('9', '⁹'));
314 }
315 } else {
316 builder.
317 append("^(").
318 append(exp.getNumerator()).
319 append('/').
320 append(exp.getDenominator()).
321 append(')');
322 }
323 }
324 }
325
326
327
328
329
330 public Unit alias(final String newName) {
331 return new Unit(newName, scale, mass, length, time, current, angle);
332 }
333
334
335
336
337
338
339 public Unit scale(final String newName, final double factor) {
340 return new Unit(newName, factor * scale, mass, length, time, current, angle);
341 }
342
343
344
345
346
347
348 public Unit power(final String newName, final Fraction exponent) {
349
350 final int num = exponent.getNumerator();
351 final int den = exponent.getDenominator();
352 double s = (num == 1) ? scale : FastMath.pow(scale, num);
353 if (den > 1) {
354 if (den == 2) {
355 s = FastMath.sqrt(s);
356 } else if (den == 3) {
357 s = FastMath.cbrt(s);
358 } else {
359 s = FastMath.pow(s, 1.0 / den);
360 }
361 }
362
363 return new Unit(newName, s,
364 mass.multiply(exponent), length.multiply(exponent),
365 time.multiply(exponent), current.multiply(current),
366 angle.multiply(exponent));
367 }
368
369
370
371
372
373 public Unit sqrt(final String newName) {
374 return new Unit(newName, FastMath.sqrt(scale),
375 mass.divide(2), length.divide(2),
376 time.divide(2), current.divide(2),
377 angle.divide(2));
378 }
379
380
381
382
383
384
385 public Unit multiply(final String newName, final Unit other) {
386 return new Unit(newName, scale * other.scale,
387 mass.add(other.mass), length.add(other.length),
388 time.add(other.time), current.add(other.current),
389 angle.add(other.angle));
390 }
391
392
393
394
395
396
397 public Unit divide(final String newName, final Unit other) {
398 return new Unit(newName, scale / other.scale,
399 mass.subtract(other.mass), length.subtract(other.length),
400 time.subtract(other.time), current.subtract(other.current),
401 angle.subtract(other.angle));
402 }
403
404
405
406
407
408 public double toSI(final double value) {
409 return value * scale;
410 }
411
412
413
414
415
416 public double toSI(final Double value) {
417 return value == null ? Double.NaN : value.doubleValue() * scale;
418 }
419
420
421
422
423
424 public double fromSI(final double value) {
425 return value / scale;
426 }
427
428
429
430
431
432 public double fromSI(final Double value) {
433 return value == null ? Double.NaN : value.doubleValue() / scale;
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511 public static Unit parse(final String unitSpecification) {
512
513
514 final List<PowerTerm> terms = Parser.buildTermsList(unitSpecification);
515
516 if (terms == null) {
517
518 return Unit.NONE;
519 }
520
521
522 Unit unit = Unit.ONE;
523 for (final PowerTerm term : terms) {
524 try {
525 Unit u = PrefixedUnit.valueOf(term.getBase().toString());
526 if (!Fraction.ONE.equals(term.getExponent())) {
527 u = u.power(null, term.getExponent());
528 }
529 u = u.scale(null, term.getScale());
530 unit = unit.multiply(null, u);
531 } catch (IllegalArgumentException iae) {
532 throw new OrekitException(OrekitMessages.UNKNOWN_UNIT, term.getBase());
533 }
534 }
535
536
537 return unit.alias(unitSpecification);
538
539 }
540
541
542
543
544
545
546
547
548 public boolean equals(final Object unit) {
549
550 if (unit == this) {
551
552 return true;
553 }
554
555 if (unit instanceof Unit) {
556 final Unit u = (Unit) unit;
557 return Precision.equals(scale, u.scale, 1) &&
558 mass.equals(u.mass) && length.equals(u.length) && time.equals(u.time) &&
559 current.equals(u.current) && angle.equals(u.angle);
560 }
561
562 return false;
563
564 }
565
566
567
568
569 public int hashCode() {
570 return 0x67e7 ^
571 (Double.hashCode(scale) << 12) ^
572 (mass.hashCode() << 10) ^
573 (length.hashCode() << 8) ^
574 (time.hashCode() << 6) ^
575 (current.hashCode() << 4) ^
576 (angle.hashCode() << 2);
577 }
578
579
580 @Override
581 public String toString() {
582 return getName();
583 }
584
585 }