1 /*
2 * Copyright (C) 2019 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.navigation.frames;
17
18 import com.irurueta.algebra.Matrix;
19 import com.irurueta.algebra.WrongSizeException;
20 import com.irurueta.geometry.InvalidRotationMatrixException;
21 import com.irurueta.geometry.MatrixRotation3D;
22 import com.irurueta.geometry.Rotation3D;
23 import com.irurueta.navigation.geodesic.Constants;
24 import com.irurueta.units.Angle;
25 import com.irurueta.units.AngleConverter;
26 import com.irurueta.units.AngleUnit;
27 import com.irurueta.units.Time;
28 import com.irurueta.units.TimeConverter;
29 import com.irurueta.units.TimeUnit;
30
31 import java.io.Serializable;
32 import java.util.Objects;
33
34 /**
35 * Contains a coordinate transformation matrix, or rotation matrix. The coordinate transformation matrix is a 3x3
36 * matrix where a vector may be transformed in one step from one set of resolving axes to another by pre-multiplying it
37 * by the appropriate coordinate transformation matrix. The coordinate transformation matrix is associated to a source
38 * and destination frame.
39 * This implementation is based on the equations defined in "Principles of GNSS, Inertial, and Multi-sensor
40 * Integrated Navigation Systems, Second Edition" and on the companion software available at:
41 * <a href="https://github.com/ymjdz/MATLAB-Codes/blob/master/Euler_to_CTM.m">
42 * https://github.com/ymjdz/MATLAB-Codes/blob/master/Euler_to_CTM.m
43 * </a>
44 * <a href="https://github.com/ymjdz/MATLAB-Codes/blob/master/CTM_to_Euler.m">
45 * https://github.com/ymjdz/MATLAB-Codes/blob/master/CTM_to_Euler.m
46 * </a>
47 */
48 public class CoordinateTransformation implements Serializable, Cloneable {
49
50 /**
51 * Number of rows of a coordinate transformation matrix.
52 */
53 public static final int ROWS = MatrixRotation3D.ROTATION3D_INHOM_MATRIX_ROWS;
54
55 /**
56 * Number of columns of a coordinate transformation matrix.
57 */
58 public static final int COLS = MatrixRotation3D.ROTATION3D_INHOM_MATRIX_ROWS;
59
60 /**
61 * Default threshold to consider a matrix valid.
62 */
63 public static final double DEFAULT_THRESHOLD = 1e-11;
64
65 /**
66 * Earth rotation rate expressed in radians per second (rad/s).
67 */
68 public static final double EARTH_ROTATION_RATE = Constants.EARTH_ROTATION_RATE;
69
70 /**
71 * 3x3 matrix containing a rotation.
72 */
73 Matrix matrix;
74
75 /**
76 * Source frame type.
77 */
78 private FrameType sourceType;
79
80 /**
81 * Destination frame type.
82 */
83 private FrameType destinationType;
84
85 /**
86 * Constructor.
87 * Initializes rotation as the identity (no rotation).
88 *
89 * @param sourceType source frame type.
90 * @param destinationType destination frame type.
91 * @throws NullPointerException if either source or destination frame types are null.
92 */
93 public CoordinateTransformation(final FrameType sourceType, final FrameType destinationType) {
94 try {
95 matrix = Matrix.identity(ROWS, COLS);
96 } catch (final WrongSizeException ignore) {
97 // never happens
98 }
99
100 setSourceType(sourceType);
101 setDestinationType(destinationType);
102 }
103
104 /**
105 * Constructor.
106 *
107 * @param matrix a 3x3 matrix containing a rotation.
108 * @param sourceType source frame type.
109 * @param destinationType destination frame type.
110 * @param threshold threshold to validate rotation matrix.
111 * @throws InvalidRotationMatrixException if provided matrix is not a valid rotation matrix (3x3 and orthonormal).
112 * @throws NullPointerException if either source or destination frame types are null.
113 * @throws IllegalArgumentException if provided threshold is negative.
114 */
115 public CoordinateTransformation(final Matrix matrix, final FrameType sourceType, final FrameType destinationType,
116 final double threshold) throws InvalidRotationMatrixException {
117 setMatrix(matrix, threshold);
118 setSourceType(sourceType);
119 setDestinationType(destinationType);
120 }
121
122 /**
123 * Constructor.
124 *
125 * @param matrix a 3x3 matrix containing a rotation.
126 * @param sourceType source frame type.
127 * @param destinationType destination frame type.
128 * @throws InvalidRotationMatrixException if provided matrix is not a valid rotation matrix (3x3 and orthonormal).
129 * @throws NullPointerException if either source or destination frame types are null.
130 */
131 public CoordinateTransformation(final Matrix matrix, final FrameType sourceType, final FrameType destinationType)
132 throws InvalidRotationMatrixException {
133 this(matrix, sourceType, destinationType, DEFAULT_THRESHOLD);
134 }
135
136 /**
137 * Constructor with Euler angles.
138 * Notice that these angles do not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
139 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
140 *
141 * @param roll roll Euler angle (around x-axis) expressed in radians.
142 * @param pitch pitch Euler angle (around y-axis) expressed in radians.
143 * @param yaw yaw Euler angle (around z-axis) expressed in radians.
144 * @param sourceType source frame type.
145 * @param destinationType destination frame type.
146 */
147 public CoordinateTransformation(final double roll, final double pitch, final double yaw,
148 final FrameType sourceType, final FrameType destinationType) {
149 this(sourceType, destinationType);
150 setEulerAngles(roll, pitch, yaw);
151 }
152
153 /**
154 * Constructor with Euler angles.
155 * Notice that these angles do not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
156 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
157 *
158 * @param roll roll Euler angle (around x-axis).
159 * @param pitch pitch Euler angle (around y-axis).
160 * @param yaw yaw Euler angle (around z-axis).
161 * @param sourceType source frame type.
162 * @param destinationType destination frame type.
163 */
164 public CoordinateTransformation(final Angle roll, final Angle pitch, final Angle yaw,
165 final FrameType sourceType, final FrameType destinationType) {
166 this(sourceType, destinationType);
167 setEulerAngles(roll, pitch, yaw);
168 }
169
170 /**
171 * Constructor with 3D rotation.
172 *
173 * @param rotation 3D rotation.
174 * @param sourceType source frame type.
175 * @param destinationType destination frame type.
176 */
177 public CoordinateTransformation(final Rotation3D rotation, final FrameType sourceType,
178 final FrameType destinationType) {
179 this(sourceType, destinationType);
180 fromRotation(rotation);
181 }
182
183 /**
184 * Constructor.
185 *
186 * @param input other coordinate transformation matrix to copy data from.
187 */
188 public CoordinateTransformation(final CoordinateTransformation input) {
189 this(input.sourceType, input.destinationType);
190 copyFrom(input);
191 }
192
193 /**
194 * Gets matrix containing a rotation.
195 *
196 * @return 3x3 matrix containing a rotation.
197 */
198 public Matrix getMatrix() {
199 Matrix result;
200 try {
201 result = new Matrix(ROWS, COLS);
202 getMatrix(result);
203 } catch (final WrongSizeException ignore) {
204 // never happens
205 result = null;
206 }
207 return result;
208 }
209
210 /**
211 * Gets matrix containing a rotation.
212 *
213 * @param result instance where internal 3x3 matrix containing a rotation will be copied to.
214 */
215 public void getMatrix(Matrix result) {
216 matrix.copyTo(result);
217 }
218
219 /**
220 * Sets matrix containing a rotation.
221 *
222 * @param matrix a 3x3 matrix containing a rotation.
223 * @param threshold threshold to validate rotation matrix.
224 * @throws InvalidRotationMatrixException if provided matrix is not a valid rotation matrix (3x3 and orthonormal).
225 * @throws IllegalArgumentException if provided threshold is negative.
226 */
227 public void setMatrix(final Matrix matrix, final double threshold) throws InvalidRotationMatrixException {
228
229 if (!isValidMatrix(matrix, threshold)) {
230 throw new InvalidRotationMatrixException();
231 }
232
233 this.matrix = matrix;
234 }
235
236 /**
237 * Sets matrix containing a rotation.
238 *
239 * @param matrix a 3x3 matrix containing a rotation.
240 * @throws InvalidRotationMatrixException if provided matrix is not a valid rotation matrix (3x3 and orthonormal).
241 */
242 public void setMatrix(final Matrix matrix) throws InvalidRotationMatrixException {
243 setMatrix(matrix, DEFAULT_THRESHOLD);
244 }
245
246 /**
247 * Determines whether provided matrix is a valid rotation matrix (3x3 and orthonormal)
248 * up to provided threshold.
249 *
250 * @param matrix matrix to be checked.
251 * @param threshold threshold to determine whether matrix is valid.
252 * @return true if matrix is valid, false otherwise.
253 * @throws IllegalArgumentException if provided threshold value is negative.
254 */
255 public static boolean isValidMatrix(final Matrix matrix, final double threshold) {
256 if (threshold < Rotation3D.MIN_THRESHOLD) {
257 throw new IllegalArgumentException();
258 }
259
260 return Rotation3D.isValidRotationMatrix(matrix, threshold);
261 }
262
263 /**
264 * Determines whether provided matrix is a valid rotation matrix (3x3 and orthonormal)
265 * up to default threshold {@link #DEFAULT_THRESHOLD}.
266 *
267 * @param matrix matrix to be checked.
268 * @return true if matrix is valid, false otherwise.
269 */
270 public static boolean isValidMatrix(final Matrix matrix) {
271 return isValidMatrix(matrix, DEFAULT_THRESHOLD);
272 }
273
274 /**
275 * Gets roll Euler angle (around x-axis) expressed in radians.
276 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
277 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
278 *
279 * @return roll Euler angle.
280 */
281 public double getRollEulerAngle() {
282 return Math.atan2(matrix.getElementAt(1, 2), matrix.getElementAt(2, 2));
283 }
284
285 /**
286 * Gets roll Euler angle (around x-axis).
287 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
288 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
289 *
290 * @param result instance where roll Euler angle will be stored.
291 */
292 public void getRollEulerAngleMeasurement(final Angle result) {
293 result.setValue(getRollEulerAngle());
294 result.setUnit(AngleUnit.RADIANS);
295 }
296
297 /**
298 * Gets roll Euler angle (around x-axis).
299 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
300 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
301 *
302 * @return roll Euler angle.
303 */
304 public Angle getRollEulerAngleMeasurement() {
305 final var result = new Angle(0.0, AngleUnit.RADIANS);
306 getRollEulerAngleMeasurement(result);
307 return result;
308 }
309
310 /**
311 * Gets pitch Euler angle (around y-axis) expressed in radians.
312 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
313 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
314 *
315 * @return pitch Euler angle.
316 */
317 public double getPitchEulerAngle() {
318 return -Math.asin(matrix.getElementAt(0, 2));
319 }
320
321 /**
322 * Gets pitch Euler angle (around y-axis).
323 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
324 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
325 *
326 * @param result instance where pitch Euler angle will be stored.
327 */
328 public void getPitchEulerAngleMeasurement(final Angle result) {
329 result.setValue(getPitchEulerAngle());
330 result.setUnit(AngleUnit.RADIANS);
331 }
332
333 /**
334 * Gets pitch Euler angle (around y-axis).
335 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
336 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
337 *
338 * @return pitch Euler angle.
339 */
340 public Angle getPitchEulerAngleMeasurement() {
341 final var result = new Angle(0.0, AngleUnit.RADIANS);
342 getPitchEulerAngleMeasurement(result);
343 return result;
344 }
345
346 /**
347 * Gets yaw Euler angle (around z-axis) expressed in radians.
348 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
349 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
350 *
351 * @return yaw Euler angle.
352 */
353 public double getYawEulerAngle() {
354 return Math.atan2(matrix.getElementAt(0, 1), matrix.getElementAt(0, 0));
355 }
356
357 /**
358 * Gets yaw Euler angle (around z-axis).
359 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
360 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
361 *
362 * @param result instance where yaw Euler angle will be stored.
363 */
364 public void getYawEulerAngleMeasurement(final Angle result) {
365 result.setValue(getYawEulerAngle());
366 result.setUnit(AngleUnit.RADIANS);
367 }
368
369 /**
370 * Gets yaw Euler angle (around z-axis).
371 * Notice that this angle does not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
372 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
373 *
374 * @return yaw Euler angle.
375 */
376 public Angle getYawEulerAngleMeasurement() {
377 final var result = new Angle(0.0, AngleUnit.RADIANS);
378 getYawEulerAngleMeasurement(result);
379 return result;
380 }
381
382 /**
383 * Sets euler angles (roll, pitch and yaw) expressed in radians.
384 * Notice that these angles do not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
385 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
386 *
387 * @param roll roll Euler angle (around x-axis) expressed in radians.
388 * @param pitch pitch Euler angle (around y-axis) expressed in radians.
389 * @param yaw yaw Euler angle (around z-axis) expressed in radians.
390 */
391 public void setEulerAngles(final double roll, final double pitch, final double yaw) {
392 final var sinPhi = Math.sin(roll);
393 final var cosPhi = Math.cos(roll);
394 final var sinTheta = Math.sin(pitch);
395 final var cosTheta = Math.cos(pitch);
396 final var sinPsi = Math.sin(yaw);
397 final var cosPsi = Math.cos(yaw);
398
399 // Calculate coordinate transformation matrix using (2.22)
400 matrix.setElementAt(0, 0, cosTheta * cosPsi);
401 matrix.setElementAt(0, 1, cosTheta * sinPsi);
402 matrix.setElementAt(0, 2, -sinTheta);
403
404 matrix.setElementAt(1, 0, -cosPhi * sinPsi + sinPhi * sinTheta * cosPsi);
405 matrix.setElementAt(1, 1, cosPhi * cosPsi + sinPhi * sinTheta * sinPsi);
406 matrix.setElementAt(1, 2, sinPhi * cosTheta);
407
408 matrix.setElementAt(2, 0, sinPhi * sinPsi + cosPhi * sinTheta * cosPsi);
409 matrix.setElementAt(2, 1, -sinPhi * cosPsi + cosPhi * sinTheta * sinPsi);
410 matrix.setElementAt(2, 2, cosPhi * cosTheta);
411 }
412
413 /**
414 * Sets euler angles (roll, pitch and yaw).
415 * Notice that these angles do not match angles obtained from {@link com.irurueta.geometry.Rotation3D} or
416 * {@link com.irurueta.geometry.Quaternion} because they are referred to different axes.
417 *
418 * @param roll roll Euler angle (around x-axis).
419 * @param pitch pitch Euler angle (around y-axis).
420 * @param yaw yaw Euler angle (around z-axis).
421 */
422 public void setEulerAngles(final Angle roll, final Angle pitch, final Angle yaw) {
423 setEulerAngles(AngleConverter.convert(roll.getValue().doubleValue(), roll.getUnit(), AngleUnit.RADIANS),
424 AngleConverter.convert(pitch.getValue().doubleValue(), pitch.getUnit(), AngleUnit.RADIANS),
425 AngleConverter.convert(yaw.getValue().doubleValue(), yaw.getUnit(), AngleUnit.RADIANS));
426 }
427
428 /**
429 * Gets source frame type.
430 *
431 * @return source frame type.
432 */
433 public FrameType getSourceType() {
434 return sourceType;
435 }
436
437 /**
438 * Sets source frame type.
439 *
440 * @param sourceType source frame type.
441 * @throws NullPointerException if provided value is null.
442 */
443 public void setSourceType(final FrameType sourceType) {
444 if (sourceType == null) {
445 throw new NullPointerException();
446 }
447
448 this.sourceType = sourceType;
449 }
450
451 /**
452 * Gets destination frame type.
453 *
454 * @return destination frame type.
455 */
456 public FrameType getDestinationType() {
457 return destinationType;
458 }
459
460 /**
461 * Sets destination frame type.
462 *
463 * @param destinationType destination frame type.
464 * @throws NullPointerException if provided value is null.
465 */
466 public void setDestinationType(final FrameType destinationType) {
467 if (destinationType == null) {
468 throw new NullPointerException();
469 }
470
471 this.destinationType = destinationType;
472 }
473
474 /**
475 * Gets internal matrix as a 3D rotation.
476 *
477 * @return a 3D rotation representing the internal matrix.
478 * @throws InvalidRotationMatrixException if internal matrix cannot be converted to a 3D rotation.
479 */
480 public Rotation3D asRotation() throws InvalidRotationMatrixException {
481 return new MatrixRotation3D(matrix);
482 }
483
484 /**
485 * Gets internal matrix as a 3D rotation.
486 *
487 * @param result instance where 3D rotation will be stored.
488 * @throws InvalidRotationMatrixException if internal matrix cannot be converted to a 3D rotation.
489 */
490 public void asRotation(final Rotation3D result) throws InvalidRotationMatrixException {
491 result.fromMatrix(matrix);
492 }
493
494 /**
495 * Sets internal matrix as the inhomogeneous matrix representation of provided 3D rotation.
496 * @param rotation 3D rotation to set matrix from.
497 */
498 public void fromRotation(final Rotation3D rotation) {
499 rotation.asInhomogeneousMatrix(matrix);
500 }
501
502 /**
503 * Copies this instance data into provided instance.
504 *
505 * @param output destination instance where data will be copied to.
506 */
507 public void copyTo(final CoordinateTransformation output) {
508 output.sourceType = sourceType;
509 output.destinationType = destinationType;
510 matrix.copyTo(output.matrix);
511 }
512
513 /**
514 * Copies data of provided instance into this instance.
515 *
516 * @param input instance to copy data from.
517 */
518 public void copyFrom(final CoordinateTransformation input) {
519 sourceType = input.sourceType;
520 destinationType = input.destinationType;
521 matrix.copyFrom(input.matrix);
522 }
523
524 /**
525 * Computes and returns hash code for this instance. Hash codes are almost unique
526 * values that are useful for fast classification and storage of objects in collections.
527 *
528 * @return Hash code.
529 */
530 @Override
531 public int hashCode() {
532 return Objects.hash(sourceType, destinationType, matrix);
533 }
534
535 /**
536 * Checks if provided object is a CoordinateTransformationMatrix having exactly the same
537 * contents as this instance.
538 *
539 * @param obj Object to be compared.
540 * @return true if both objects are considered to be equal, false otherwise.
541 */
542 @Override
543 public boolean equals(final Object obj) {
544 if (obj == null) {
545 return false;
546 }
547 if (obj == this) {
548 return true;
549 }
550 if (!(obj instanceof CoordinateTransformation other)) {
551 return false;
552 }
553
554 return equals(other);
555 }
556
557 /**
558 * Checks if provided instance has exactly the same contents as this instance.
559 *
560 * @param other instance to be compared.
561 * @return true if both instances are considered to be equal, false otherwise.
562 */
563 public boolean equals(final CoordinateTransformation other) {
564 return equals(other, 0.0);
565 }
566
567 /**
568 * Checks if provided instance has contents similar to this instance up to
569 * provided threshold value.
570 *
571 * @param other instance to be compared.
572 * @param threshold maximum difference allowed between values on internal matrix.
573 * @return true if both instances are considered to be equal (up to provided threshold),
574 * false otherwise.
575 */
576 public boolean equals(final CoordinateTransformation other, final double threshold) {
577 if (other == null) {
578 return false;
579 }
580 return other.sourceType == sourceType && other.destinationType == destinationType &&
581 other.matrix.equals(matrix, threshold);
582 }
583
584 /**
585 * Computes the inverse of this coordinate transformation matrix and stores the result into provided instance.
586 *
587 * @param result instance where inverse will be stored.
588 */
589 public void inverse(final CoordinateTransformation result) {
590 try {
591 final var source = sourceType;
592 final var destination = destinationType;
593 final var m = Matrix.identity(ROWS, COLS);
594 m.copyFrom(this.matrix);
595
596 result.setSourceType(destination);
597 result.setDestinationType(source);
598
599 // Because matrix needs to be a rotation (3x3 and orthonormal), its inverse is the transpose
600 m.transpose();
601 result.setMatrix(m);
602 } catch (final WrongSizeException | InvalidRotationMatrixException ignore) {
603 // never happens
604 }
605 }
606
607 /**
608 * Converts this instance into its inverse coordinate transformation matrix.
609 */
610 public void inverse() {
611 inverse(this);
612 }
613
614 /**
615 * Computes the inverse of this coordinate transformation matrix and returns it as a new instance.
616 *
617 * @return the inverse of this coordinate transformation matrix.
618 */
619 public CoordinateTransformation inverseAndReturnNew() {
620 final var result = new CoordinateTransformation(destinationType, sourceType);
621 inverse(result);
622 return result;
623 }
624
625 /**
626 * Computes matrix to convert ECEF to NED coordinates.
627 *
628 * @param latitude latitude angle.
629 * @param longitude longitude angle.
630 * @param result instance where computed matrix will be stored.
631 */
632 public static void ecefToNedMatrix(final Angle latitude, final Angle longitude, final Matrix result) {
633 ecefToNedMatrix(
634 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
635 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS),
636 result);
637 }
638
639 /**
640 * Computes matrix to convert ECEF to NED coordinates.
641 *
642 * @param latitude latitude angle.
643 * @param longitude longitude angle.
644 * @return a new matrix to convert ECEF to NED coordinates.
645 */
646 public static Matrix ecefToNedMatrix(final Angle latitude, final Angle longitude) {
647 return ecefToNedMatrix(
648 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
649 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS));
650 }
651
652 /**
653 * Computes matrix to convert ECEF to NED coordinates.
654 *
655 * @param latitude latitude expressed in radians.
656 * @param longitude longitude expressed in radians.
657 * @param result instance where computed matrix will be stored.
658 */
659 public static void ecefToNedMatrix(final double latitude, final double longitude, final Matrix result) {
660 if (result.getRows() != ROWS || result.getColumns() != COLS) {
661 try {
662 result.resize(ROWS, COLS);
663 } catch (final WrongSizeException ignore) {
664 // never happens
665 }
666 }
667 // Calculate ECEF to NED coordinate transformation matrix using (2.150)
668 final var cosLat = Math.cos(latitude);
669 final var sinLat = Math.sin(latitude);
670 final var cosLong = Math.cos(longitude);
671 final var sinLong = Math.sin(longitude);
672
673 result.setElementAtIndex(0, -sinLat * cosLong);
674 result.setElementAtIndex(1, -sinLong);
675 result.setElementAtIndex(2, -cosLat * cosLong);
676
677 result.setElementAtIndex(3, -sinLat * sinLong);
678 result.setElementAtIndex(4, cosLong);
679 result.setElementAtIndex(5, -cosLat * sinLong);
680
681 result.setElementAtIndex(6, cosLat);
682 result.setElementAtIndex(7, 0.0);
683 result.setElementAtIndex(8, -sinLat);
684 }
685
686 /**
687 * Computes matrix to convert ECEF to NED coordinates.
688 *
689 * @param latitude latitude expressed in radians.
690 * @param longitude longitude expressed in radians.
691 * @return a new matrix to convert ECEF to NED coordinates.
692 */
693 public static Matrix ecefToNedMatrix(final double latitude, final double longitude) {
694 Matrix result;
695 try {
696 result = new Matrix(CoordinateTransformation.ROWS, CoordinateTransformation.COLS);
697 ecefToNedMatrix(latitude, longitude, result);
698 } catch (final WrongSizeException ignore) {
699 // never happens
700 result = null;
701 }
702 return result;
703 }
704
705 /**
706 * Computes ECEF to NED coordinate transformation matrix.
707 *
708 * @param latitude latitude angle.
709 * @param longitude longitude angle.
710 * @param result instance where result will be stored.
711 */
712 public static void ecefToNedCoordinateTransformationMatrix(
713 final Angle latitude, final Angle longitude, final CoordinateTransformation result) {
714 ecefToNedCoordinateTransformationMatrix(
715 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
716 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS),
717 result);
718 }
719
720 /**
721 * Computes ECEF to NED coordinate transformation matrix.
722 *
723 * @param latitude latitude angle.
724 * @param longitude longitude angle.
725 * @return a new ECEF to NED coordinate transformation matrix.
726 */
727 public static CoordinateTransformation ecefToNedCoordinateTransformationMatrix(
728 final Angle latitude, final Angle longitude) {
729 return ecefToNedCoordinateTransformationMatrix(
730 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
731 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS));
732 }
733
734 /**
735 * Computes ECEF to NED coordinate transformation matrix.
736 *
737 * @param latitude latitude expressed in radians.
738 * @param longitude longitude expressed in radians.
739 * @param result instance where result will be stored.
740 */
741 public static void ecefToNedCoordinateTransformationMatrix(
742 final double latitude, final double longitude, final CoordinateTransformation result) {
743 try {
744 result.setSourceType(FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
745 result.setDestinationType(FrameType.LOCAL_NAVIGATION_FRAME);
746 result.setMatrix(ecefToNedMatrix(latitude, longitude));
747 } catch (final InvalidRotationMatrixException ignore) {
748 // never happens
749 }
750 }
751
752 /**
753 * Computes ECEF to NED coordinate transformation matrix.
754 *
755 * @param latitude latitude expressed in radians.
756 * @param longitude longitude expressed in radians.
757 * @return a new ECEF to NED coordinate transformation matrix.
758 */
759 public static CoordinateTransformation ecefToNedCoordinateTransformationMatrix(
760 final double latitude, final double longitude) {
761 final var result = new CoordinateTransformation(FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME,
762 FrameType.LOCAL_NAVIGATION_FRAME);
763 ecefToNedCoordinateTransformationMatrix(latitude, longitude, result);
764 return result;
765 }
766
767 /**
768 * Computes matrix to convert NED to ECEF coordinates.
769 *
770 * @param latitude latitude angle.
771 * @param longitude longitude angle.
772 * @param result instance where computed matrix will be stored.
773 */
774 public static void nedToEcefMatrix(final Angle latitude, final Angle longitude, final Matrix result) {
775 nedToEcefMatrix(
776 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
777 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS),
778 result);
779 }
780
781 /**
782 * Computes matrix to convert NED to ECEF coordinates.
783 *
784 * @param latitude latitude angle.
785 * @param longitude longitude angle.
786 * @return a new matrix to convert NED to ECEF coordinates.
787 */
788 public static Matrix nedToEcefMatrix(final Angle latitude, final Angle longitude) {
789 return nedToEcefMatrix(
790 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
791 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS));
792 }
793
794 /**
795 * Computes matrix to convert NED to ECEF coordinates.
796 *
797 * @param latitude latitude expressed in radians.
798 * @param longitude longitude expressed in radians.
799 * @param result instance where computed matrix will be stored.
800 */
801 public static void nedToEcefMatrix(final double latitude, final double longitude, final Matrix result) {
802 // NED to ECEF matrix is the inverse of ECEF to NED matrix.
803 // Since ECEF to NED matrix is a rotation (3x3 and orthonormal), its inverse is the transpose.
804 ecefToNedMatrix(latitude, longitude, result);
805 result.transpose();
806 }
807
808 /**
809 * Computes matrix to convert NED to ECEF coordinates.
810 *
811 * @param latitude latitude expressed in radians.
812 * @param longitude longitude expressed in radians.
813 * @return a new matrix to convert NED to ECEF coordinates.
814 */
815 public static Matrix nedToEcefMatrix(final double latitude, final double longitude) {
816 Matrix result;
817 try {
818 result = new Matrix(CoordinateTransformation.ROWS, CoordinateTransformation.COLS);
819 nedToEcefMatrix(latitude, longitude, result);
820 } catch (final WrongSizeException ignore) {
821 // never happens
822 result = null;
823 }
824
825 return result;
826 }
827
828 /**
829 * Computes NED to ECEF coordinate transformation matrix.
830 *
831 * @param latitude latitude angle.
832 * @param longitude longitude angle.
833 * @param result instance where result will be stored.
834 */
835 public static void nedToEcefCoordinateTransformationMatrix(
836 final Angle latitude, final Angle longitude, final CoordinateTransformation result) {
837 nedToEcefCoordinateTransformationMatrix(
838 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
839 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS),
840 result);
841 }
842
843 /**
844 * Computes NED to ECEF coordinate transformation matrix.
845 *
846 * @param latitude latitude angle.
847 * @param longitude longitude angle.
848 * @return a new NED to ECEF coordinate transformation matrix.
849 */
850 public static CoordinateTransformation nedToEcefCoordinateTransformationMatrix(
851 final Angle latitude, final Angle longitude) {
852 return nedToEcefCoordinateTransformationMatrix(
853 AngleConverter.convert(latitude.getValue().doubleValue(), latitude.getUnit(), AngleUnit.RADIANS),
854 AngleConverter.convert(longitude.getValue().doubleValue(), longitude.getUnit(), AngleUnit.RADIANS));
855 }
856
857 /**
858 * Computes NED to ECEF coordinate transformation matrix.
859 *
860 * @param latitude latitude expressed in radians.
861 * @param longitude longitude expressed in radians.
862 * @param result instance where result will be stored.
863 */
864 public static void nedToEcefCoordinateTransformationMatrix(
865 final double latitude, final double longitude, final CoordinateTransformation result) {
866 try {
867 result.setSourceType(FrameType.LOCAL_NAVIGATION_FRAME);
868 result.setDestinationType(FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
869 result.setMatrix(nedToEcefMatrix(latitude, longitude));
870 } catch (final InvalidRotationMatrixException ignore) {
871 // never happens
872 }
873 }
874
875 /**
876 * Computes NED to ECEF coordinate transformation matrix.
877 *
878 * @param latitude latitude expressed in radians.
879 * @param longitude longitude expressed in radians.
880 * @return a new NED to ECEF coordinate transformation matrix.
881 */
882 public static CoordinateTransformation nedToEcefCoordinateTransformationMatrix(
883 final double latitude, final double longitude) {
884 final var result = new CoordinateTransformation(FrameType.LOCAL_NAVIGATION_FRAME,
885 FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
886 nedToEcefCoordinateTransformationMatrix(latitude, longitude, result);
887 return result;
888 }
889
890 /**
891 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
892 * rotation during provided time interval.
893 *
894 * @param timeInterval a time interval.
895 * @param result instance where result will be stored.
896 */
897 public static void ecefToEciMatrixFromTimeInterval(final Time timeInterval, final Matrix result) {
898 ecefToEciMatrixFromTimeInterval(TimeConverter.convert(
899 timeInterval.getValue().doubleValue(), timeInterval.getUnit(), TimeUnit.SECOND), result);
900 }
901
902 /**
903 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
904 * rotation during provided time interval.
905 *
906 * @param timeInterval a time interval expressed in seconds (s).
907 * @param result instance where result will be stored.
908 */
909 public static void ecefToEciMatrixFromTimeInterval(final double timeInterval, final Matrix result) {
910 ecefToEciMatrixFromAngle(EARTH_ROTATION_RATE * timeInterval, result);
911 }
912
913 /**
914 * Computes ECEF to ECI coordinate transformation matrix for provided Earth
915 * rotation angle.
916 *
917 * @param angle angle amount the Earth has rotated.
918 * @param result instance where result will be stored.
919 */
920 public static void ecefToEciMatrixFromAngle(final Angle angle, final Matrix result) {
921 ecefToEciMatrixFromAngle(AngleConverter.convert(angle.getValue().doubleValue(), angle.getUnit(),
922 AngleUnit.RADIANS), result);
923 }
924
925 /**
926 * Computes ECEF to ECI coordinate transformation matrix for provided Earth
927 * rotation angle.
928 *
929 * @param angle angle amount the Earth has rotated expressed in radians.
930 * @param result instance where result will be stored.
931 */
932 public static void ecefToEciMatrixFromAngle(final double angle, final Matrix result) {
933 if (result.getRows() != ROWS || result.getColumns() != COLS) {
934 try {
935 result.resize(ROWS, COLS);
936 } catch (final WrongSizeException ignore) {
937 // never happens
938 }
939 }
940
941 final var sinAngle = Math.sin(angle);
942 final var cosAngle = Math.cos(angle);
943
944 result.setElementAt(0, 0, cosAngle);
945 result.setElementAt(0, 1, -sinAngle);
946 result.setElementAt(0, 2, 0.0);
947
948 result.setElementAt(1, 0, sinAngle);
949 result.setElementAt(1, 1, cosAngle);
950 result.setElementAt(1, 2, 0.0);
951
952 result.setElementAt(2, 0, 0.0);
953 result.setElementAt(2, 1, 0.0);
954 result.setElementAt(2, 2, 1.0);
955 }
956
957 /**
958 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
959 * rotation during provided time interval.
960 *
961 * @param timeInterval a time interval.
962 * @return a new ECEF to ECI coordinate transformation matrix.
963 */
964 public static Matrix ecefToEciMatrixFromTimeInterval(final Time timeInterval) {
965 return ecefToEciMatrixFromTimeInterval(TimeConverter.convert(
966 timeInterval.getValue().doubleValue(), timeInterval.getUnit(), TimeUnit.SECOND));
967 }
968
969 /**
970 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
971 * rotation during provided time interval.
972 *
973 * @param timeInterval a time interval expressed in seconds (s).
974 * @return a new ECEF to ECI coordinate transformation matrix.
975 */
976 public static Matrix ecefToEciMatrixFromTimeInterval(final double timeInterval) {
977 return ecefToEciMatrixFromAngle(EARTH_ROTATION_RATE * timeInterval);
978 }
979
980 /**
981 * Computes ECEF to ECI coordinate transformation matrix for provided Earth
982 * rotation angle.
983 *
984 * @param angle angle amount the Earth has rotated.
985 * @return a new ECEF to ECI coordinate transformation matrix.
986 */
987 public static Matrix ecefToEciMatrixFromAngle(final Angle angle) {
988 return ecefToEciMatrixFromAngle(AngleConverter.convert(angle.getValue().doubleValue(), angle.getUnit(),
989 AngleUnit.RADIANS));
990 }
991
992 /**
993 * Computes ECEF to ECI coordinate transformation matrix for provided Earth
994 * rotation angle.
995 *
996 * @param angle angle amount the Earth has rotated expressed in radians.
997 * @return a new ECEF to ECI coordinate transformation matrix.
998 */
999 public static Matrix ecefToEciMatrixFromAngle(final double angle) {
1000 Matrix result;
1001 try {
1002 result = new Matrix(CoordinateTransformation.ROWS, CoordinateTransformation.COLS);
1003 ecefToEciMatrixFromAngle(angle, result);
1004 } catch (final WrongSizeException ignore) {
1005 // never happens
1006 result = null;
1007 }
1008 return result;
1009 }
1010
1011 /**
1012 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
1013 * rotation during provided time interval.
1014 *
1015 * @param timeInterval a time interval.
1016 * @param result instance where result will be stored.
1017 */
1018 public static void ecefToEciCoordinateTransformationMatrixFromTimeInterval(
1019 final Time timeInterval, final CoordinateTransformation result) {
1020 ecefToEciCoordinateTransformationMatrixFromTimeInterval(
1021 TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(), TimeUnit.SECOND),
1022 result);
1023 }
1024
1025 /**
1026 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
1027 * rotation during provided time interval.
1028 *
1029 * @param timeInterval a time interval expressed in seconds (s).
1030 * @param result instance where result will be stored.
1031 */
1032 public static void ecefToEciCoordinateTransformationMatrixFromTimeInterval(
1033 final double timeInterval, final CoordinateTransformation result) {
1034 ecefToEciCoordinateTransformationMatrixFromAngle(EARTH_ROTATION_RATE * timeInterval, result);
1035 }
1036
1037 /**
1038 * Computes ECEF to ECI coordinate transformation matrix for provided Earth
1039 * rotation angle.
1040 *
1041 * @param angle angle amount the Earth has rotated.
1042 * @param result instance where result will be stored.
1043 */
1044 public static void ecefToEciCoordinateTransformationMatrixFromAngle(
1045 final Angle angle, final CoordinateTransformation result) {
1046 ecefToEciCoordinateTransformationMatrixFromAngle(
1047 AngleConverter.convert(angle.getValue().doubleValue(), angle.getUnit(), AngleUnit.RADIANS), result);
1048 }
1049
1050 /**
1051 * Computes ECEF to ECI coordinate transformation matrix for provided Earth
1052 * rotation angle.
1053 *
1054 * @param angle angle amount the Earth has rotated expressed in radians.
1055 * @param result instance where result will be stored.
1056 */
1057 public static void ecefToEciCoordinateTransformationMatrixFromAngle(
1058 final double angle, final CoordinateTransformation result) {
1059 try {
1060 result.setSourceType(FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
1061 result.setDestinationType(FrameType.EARTH_CENTERED_INERTIAL_FRAME);
1062 result.setMatrix(ecefToEciMatrixFromAngle(angle));
1063 } catch (final InvalidRotationMatrixException ignore) {
1064 // never happens
1065 }
1066 }
1067
1068 /**
1069 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
1070 * rotation during provided time interval.
1071 *
1072 * @param timeInterval a time interval.
1073 * @return a new ECEF to ECI coordinate transformation matrix.
1074 */
1075 public static CoordinateTransformation ecefToEciCoordinateTransformationMatrixFromTimeInterval(
1076 final Time timeInterval) {
1077 final var result = new CoordinateTransformation(
1078 FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME, FrameType.EARTH_CENTERED_INERTIAL_FRAME);
1079 ecefToEciCoordinateTransformationMatrixFromTimeInterval(timeInterval, result);
1080 return result;
1081 }
1082
1083 /**
1084 * Computes ECEF to ECI coordinate transformation matrix taking into account Earth
1085 * rotation during provided time interval.
1086 *
1087 * @param timeInterval a time interval expressed in seconds (s).
1088 * @return a new ECEF to ECI coordinate transformation matrix.
1089 */
1090 public static CoordinateTransformation ecefToEciCoordinateTransformationMatrixFromTimeInterval(
1091 final double timeInterval) {
1092 final var result = new CoordinateTransformation(
1093 FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME, FrameType.EARTH_CENTERED_INERTIAL_FRAME);
1094 ecefToEciCoordinateTransformationMatrixFromTimeInterval(timeInterval, result);
1095 return result;
1096 }
1097
1098 /**
1099 * Computes ECEF to ECI coordinate transformation matrix for provided Earth rotation angle.
1100 *
1101 * @param angle angle amount the Earth has rotated.
1102 * @return a new ECEF to ECI coordinate transformation matrix.
1103 */
1104 public static CoordinateTransformation ecefToEciCoordinateTransformationMatrixFromAngle(final Angle angle) {
1105 final var result = new CoordinateTransformation(
1106 FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME, FrameType.EARTH_CENTERED_INERTIAL_FRAME);
1107 ecefToEciCoordinateTransformationMatrixFromAngle(angle, result);
1108 return result;
1109 }
1110
1111 /**
1112 * Computes ECEF to ECI coordinate transformation matrix for provided Earth rotation angle.
1113 *
1114 * @param angle angle amount the Earth has rotated expressed in radians.
1115 * @return a new ECEF to ECI coordinate transformation matrix.
1116 */
1117 public static CoordinateTransformation ecefToEciCoordinateTransformationMatrixFromAngle(final double angle) {
1118 final var result = new CoordinateTransformation(
1119 FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME, FrameType.EARTH_CENTERED_INERTIAL_FRAME);
1120 ecefToEciCoordinateTransformationMatrixFromAngle(angle, result);
1121 return result;
1122 }
1123
1124 /**
1125 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1126 * rotation during provided time interval.
1127 *
1128 * @param timeInterval a time interval.
1129 * @param result instance where result will be stored.
1130 */
1131 public static void eciToEcefMatrixFromTimeInterval(final Time timeInterval, final Matrix result) {
1132 eciToEcefMatrixFromTimeInterval(TimeConverter.convert(
1133 timeInterval.getValue().doubleValue(), timeInterval.getUnit(), TimeUnit.SECOND), result);
1134 }
1135
1136 /**
1137 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1138 * rotation during provided time interval.
1139 *
1140 * @param timeInterval a time interval expressed in seconds (s).
1141 * @param result instance where result will be stored.
1142 */
1143 public static void eciToEcefMatrixFromTimeInterval(final double timeInterval, final Matrix result) {
1144 eciToEcefMatrixFromAngle(EARTH_ROTATION_RATE * timeInterval, result);
1145 }
1146
1147 /**
1148 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1149 * rotation angle.
1150 *
1151 * @param angle angle amount the Earth has rotated.
1152 * @param result instance where result will be stored.
1153 */
1154 public static void eciToEcefMatrixFromAngle(final Angle angle, final Matrix result) {
1155 eciToEcefMatrixFromAngle(AngleConverter.convert(angle.getValue().doubleValue(), angle.getUnit(),
1156 AngleUnit.RADIANS), result);
1157 }
1158
1159 /**
1160 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1161 * rotation angle.
1162 *
1163 * @param angle angle amount the Earth has rotated expressed in radians.
1164 * @param result instance where result will be stored.
1165 */
1166 public static void eciToEcefMatrixFromAngle(final double angle, final Matrix result) {
1167 if (result.getRows() != ROWS || result.getColumns() != COLS) {
1168 try {
1169 result.resize(ROWS, COLS);
1170 } catch (final WrongSizeException ignore) {
1171 // never happens
1172 }
1173 }
1174
1175 final var sinAngle = Math.sin(angle);
1176 final var cosAngle = Math.cos(angle);
1177
1178 result.setElementAt(0, 0, cosAngle);
1179 result.setElementAt(0, 1, sinAngle);
1180 result.setElementAt(0, 2, 0.0);
1181
1182 result.setElementAt(1, 0, -sinAngle);
1183 result.setElementAt(1, 1, cosAngle);
1184 result.setElementAt(1, 2, 0.0);
1185
1186 result.setElementAt(2, 0, 0.0);
1187 result.setElementAt(2, 1, 0.0);
1188 result.setElementAt(2, 2, 1.0);
1189 }
1190
1191 /**
1192 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1193 * rotation during provided time interval.
1194 *
1195 * @param timeInterval a time interval.
1196 * @return a new ECI to ECEF coordinate transformation matrix.
1197 */
1198 public static Matrix eciToEcefMatrixFromTimeInterval(final Time timeInterval) {
1199 return eciToEcefMatrixFromTimeInterval(TimeConverter.convert(
1200 timeInterval.getValue().doubleValue(), timeInterval.getUnit(), TimeUnit.SECOND));
1201 }
1202
1203 /**
1204 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1205 * rotation during provided time interval.
1206 *
1207 * @param timeInterval a time interval expressed in seconds (s).
1208 * @return a new ECI to ECEF coordinate transformation matrix.
1209 */
1210 public static Matrix eciToEcefMatrixFromTimeInterval(final double timeInterval) {
1211 return eciToEcefMatrixFromAngle(EARTH_ROTATION_RATE * timeInterval);
1212 }
1213
1214 /**
1215 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1216 * rotation angle.
1217 *
1218 * @param angle angle amount the Earth has rotated.
1219 * @return a new ECI to ECEF coordinate transformation matrix.
1220 */
1221 public static Matrix eciToEcefMatrixFromAngle(final Angle angle) {
1222 return eciToEcefMatrixFromAngle(AngleConverter.convert(
1223 angle.getValue().doubleValue(), angle.getUnit(), AngleUnit.RADIANS));
1224 }
1225
1226 /**
1227 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1228 * rotation angle.
1229 *
1230 * @param angle angle amount the Earth has rotated.
1231 * @return a new ECI to ECEF coordinate transformation matrix.
1232 */
1233 public static Matrix eciToEcefMatrixFromAngle(final double angle) {
1234 Matrix result;
1235 try {
1236 result = new Matrix(CoordinateTransformation.ROWS, CoordinateTransformation.COLS);
1237 eciToEcefMatrixFromAngle(angle, result);
1238 } catch (final WrongSizeException ignore) {
1239 // never happens
1240 result = null;
1241 }
1242 return result;
1243 }
1244
1245 /**
1246 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1247 * rotation during provided time interval.
1248 *
1249 * @param timeInterval a time interval.
1250 * @param result instance where result will be stored.
1251 */
1252 public static void eciToEcefCoordinateTransformationMatrixFromTimeInterval(
1253 final Time timeInterval, final CoordinateTransformation result) {
1254 eciToEcefCoordinateTransformationMatrixFromTimeInterval(
1255 TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(), TimeUnit.SECOND),
1256 result);
1257 }
1258
1259 /**
1260 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1261 * rotation during provided time interval.
1262 *
1263 * @param timeInterval a time interval expressed in seconds (s).
1264 * @param result instance where result will be stored.
1265 */
1266 public static void eciToEcefCoordinateTransformationMatrixFromTimeInterval(
1267 final double timeInterval, final CoordinateTransformation result) {
1268 eciToEcefCoordinateTransformationMatrixFromAngle(EARTH_ROTATION_RATE * timeInterval, result);
1269 }
1270
1271 /**
1272 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1273 * rotation angle.
1274 *
1275 * @param angle angle amount the Earth has rotated.
1276 * @param result instance where result will be stored.
1277 */
1278 public static void eciToEcefCoordinateTransformationMatrixFromAngle(
1279 final Angle angle, final CoordinateTransformation result) {
1280 eciToEcefCoordinateTransformationMatrixFromAngle(
1281 AngleConverter.convert(angle.getValue().doubleValue(), angle.getUnit(), AngleUnit.RADIANS), result);
1282 }
1283
1284 /**
1285 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1286 * rotation angle.
1287 *
1288 * @param angle angle amount the Earth has rotated expressed in radians.
1289 * @param result instance where result will be stored.
1290 */
1291 public static void eciToEcefCoordinateTransformationMatrixFromAngle(
1292 final double angle, final CoordinateTransformation result) {
1293 try {
1294 result.setSourceType(FrameType.EARTH_CENTERED_INERTIAL_FRAME);
1295 result.setDestinationType(FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
1296 result.setMatrix(eciToEcefMatrixFromAngle(angle));
1297 } catch (final InvalidRotationMatrixException ignore) {
1298 // never happens
1299 }
1300 }
1301
1302 /**
1303 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1304 * rotation during provided time interval.
1305 *
1306 * @param timeInterval a time interval.
1307 * @return a new ECI to ECEF coordinate transformation matrix.
1308 */
1309 public static CoordinateTransformation eciToEcefCoordinateTransformationMatrixFromTimeInterval(
1310 final Time timeInterval) {
1311 final var result = new CoordinateTransformation(
1312 FrameType.EARTH_CENTERED_INERTIAL_FRAME, FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
1313 eciToEcefCoordinateTransformationMatrixFromTimeInterval(timeInterval, result);
1314 return result;
1315 }
1316
1317 /**
1318 * Computes ECI to ECEF coordinate transformation matrix taking into account Earth
1319 * rotation during provided time interval.
1320 *
1321 * @param timeInterval a time interval expressed in seconds (s).
1322 * @return a new ECI to ECEF coordinate transformation matrix.
1323 */
1324 public static CoordinateTransformation eciToEcefCoordinateTransformationMatrixFromInterval(
1325 final double timeInterval) {
1326 final var result = new CoordinateTransformation(
1327 FrameType.EARTH_CENTERED_INERTIAL_FRAME, FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
1328 eciToEcefCoordinateTransformationMatrixFromTimeInterval(timeInterval, result);
1329 return result;
1330 }
1331
1332 /**
1333 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1334 * rotation angle.
1335 *
1336 * @param angle angle amount the Earth has rotated.
1337 * @return a new ECI to ECEF coordinate transformation matrix.
1338 */
1339 public static CoordinateTransformation eciToEcefCoordinateTransformationMatrixFromAngle(
1340 final Angle angle) {
1341 final var result = new CoordinateTransformation(
1342 FrameType.EARTH_CENTERED_INERTIAL_FRAME, FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
1343 eciToEcefCoordinateTransformationMatrixFromAngle(angle, result);
1344 return result;
1345 }
1346
1347 /**
1348 * Computes ECI to ECEF coordinate transformation matrix for provided Earth
1349 * rotation angle.
1350 *
1351 * @param angle angle amount the Earth has rotated expressed in radians.
1352 * @return a new ECI to ECEF coordinate transformation matrix.
1353 */
1354 public static CoordinateTransformation eciToEcefCoordinateTransformationMatrixFromAngle(
1355 final double angle) {
1356 final var result = new CoordinateTransformation(
1357 FrameType.EARTH_CENTERED_INERTIAL_FRAME, FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
1358 eciToEcefCoordinateTransformationMatrixFromAngle(angle, result);
1359 return result;
1360 }
1361
1362 /**
1363 * Makes a copy of this instance.
1364 *
1365 * @return a copy of this instance.
1366 * @throws CloneNotSupportedException if clone fails for some reason.
1367 */
1368 @Override
1369 protected Object clone() throws CloneNotSupportedException {
1370 final var result = (CoordinateTransformation) super.clone();
1371 copyTo(result);
1372 return result;
1373 }
1374 }