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