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