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.InhomogeneousPoint3D;
21 import com.irurueta.geometry.InvalidRotationMatrixException;
22 import com.irurueta.geometry.Point3D;
23 import com.irurueta.geometry.Rotation3D;
24 import com.irurueta.units.Distance;
25 import com.irurueta.units.DistanceConverter;
26 import com.irurueta.units.DistanceUnit;
27 import com.irurueta.units.Speed;
28 import com.irurueta.units.SpeedConverter;
29 import com.irurueta.units.SpeedUnit;
30
31 import java.io.Serializable;
32 import java.util.Objects;
33
34 /**
35 * Base class for ECI or ECEF frames containing common logic and data for such frames.
36 */
37 public abstract class ECIorECEFFrame<T extends ECIorECEFFrame<?>> implements Frame, Serializable {
38
39 /**
40 * Number of coordinates representing position.
41 */
42 public static final int NUM_POSITION_COORDINATES = 3;
43
44 /**
45 * Number of coordinates representing velocity.
46 */
47 public static final int NUM_VELOCITY_COORDINATES = 3;
48
49 /**
50 * Cartesian x coordinate of body position expressed in meters (m) with respect ECI or ECEF frame, resolved along
51 * the corresponding frame axes.
52 */
53 double x;
54
55 /**
56 * Cartesian y coordinate of body position expressed in meters (m) with respect ECI or ECEF frame, resolved along
57 * the corresponding frame axes.
58 */
59 double y;
60
61 /**
62 * Cartesian z coordinate of body position expressed in meters (m) with respect ECI or ECEF frame, resolved along
63 * the corresponding frame axes.
64 */
65 double z;
66
67 /**
68 * X coordinate of velocity of body frame expressed in meters per second (m/s) with respect ECI or ECEF frame,
69 * resolved along the corresponding frame axes.
70 */
71 double vx;
72
73 /**
74 * Y coordinate of velocity of body frame expressed in meters per second (m/s) with respect ECI or ECEF frame,
75 * resolved along the corresponding frame axes.
76 */
77 double vy;
78
79 /**
80 * Z coordinate of velocity of body frame expressed in meters per second (m/s) with respect ECI or ECEF frame,
81 * resolved along the corresponding frame axes.
82 */
83 double vz;
84
85 /**
86 * Body to ECI frame coordinate transformation matrix.
87 */
88 CoordinateTransformation c;
89
90 /**
91 * Actual type class
92 */
93 transient Class<T> clazz;
94
95 /**
96 * Constructor.
97 */
98 ECIorECEFFrame(final Class<T> c) {
99 clazz = c;
100 }
101
102 /**
103 * Constructor.
104 *
105 * @param c Body to ECI or ECEF coordinate transformation.
106 * @throws InvalidSourceAndDestinationFrameTypeException if source or destination frame types are invalid.
107 */
108 ECIorECEFFrame(final CoordinateTransformation c) throws InvalidSourceAndDestinationFrameTypeException {
109 setCoordinateTransformation(c);
110 }
111
112 /**
113 * Gets cartesian x coordinate of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
114 *
115 * @return cartesian x coordinate of body position.
116 */
117 public double getX() {
118 return x;
119 }
120
121 /**
122 * Sets cartesian x coordinate of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
123 *
124 * @param x cartesian x coordinate of body position.
125 */
126 public void setX(final double x) {
127 this.x = x;
128 }
129
130 /**
131 * Gets cartesian y coordinate of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
132 *
133 * @return cartesian y coordinate of body position.
134 */
135 public double getY() {
136 return y;
137 }
138
139 /**
140 * Sets cartesian y coordinate of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
141 *
142 * @param y cartesian y coordinate of body position.
143 */
144 public void setY(final double y) {
145 this.y = y;
146 }
147
148 /**
149 * Gets cartesian z coordinate of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
150 *
151 * @return cartesian z coordinate of body position.
152 */
153 public double getZ() {
154 return z;
155 }
156
157 /**
158 * Sets cartesian z coordinate of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
159 *
160 * @param z cartesian z coordinate of body position.
161 */
162 public void setZ(final double z) {
163 this.z = z;
164 }
165
166 /**
167 * Sets cartesian coordinates of body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
168 *
169 * @param x cartesian x coordinate of body position, resolved along ECI or ECEF-frame axes.
170 * @param y cartesian y coordinate of body position, resolved along ECI or ECEF-frame axes.
171 * @param z cartesian z coordinate of body position, resolved along ECI or ECEF-frame axes.
172 */
173 public void setCoordinates(final double x, final double y, final double z) {
174 this.x = x;
175 this.y = y;
176 this.z = z;
177 }
178
179 /**
180 * Gets body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
181 *
182 * @return body position.
183 */
184 public Point3D getPosition() {
185 return new InhomogeneousPoint3D(x, y, z);
186 }
187
188 /**
189 * Gets body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
190 *
191 * @param result instance where position data is copied to.
192 */
193 public void getPosition(final Point3D result) {
194 result.setInhomogeneousCoordinates(x, y, z);
195 }
196
197 /**
198 * Sets body position expressed in meters (m) and resolved along ECI or ECEF-frame axes.
199 *
200 * @param point body position to be set.
201 */
202 public void setPosition(final Point3D point) {
203 x = point.getInhomX();
204 y = point.getInhomY();
205 z = point.getInhomZ();
206 }
207
208 /**
209 * Gets cartesian x coordinate of body position resolved along ECI or ECEF-frame axes.
210 *
211 * @param result instance where cartesian x coordinate of body position will be stored.
212 */
213 public void getPositionX(final Distance result) {
214 result.setValue(x);
215 result.setUnit(DistanceUnit.METER);
216 }
217
218 /**
219 * Gets cartesian x coordinate of body position resolved along ECI or ECEF-frame axes.
220 *
221 * @return x coordinate of body position resolved along ECI or ECEF-frame axes.
222 */
223 public Distance getPositionX() {
224 return new Distance(x, DistanceUnit.METER);
225 }
226
227 /**
228 * Sets cartesian x coordinate of body position resolved along ECI or ECEF-frame axes.
229 *
230 * @param positionX cartesian x coordinate of body position to be set.
231 */
232 public void setPositionX(final Distance positionX) {
233 x = DistanceConverter.convert(positionX.getValue().doubleValue(), positionX.getUnit(), DistanceUnit.METER);
234 }
235
236 /**
237 * Gets cartesian y coordinate of body position resolved along ECI or ECEF-frame axes.
238 *
239 * @param result instance where cartesian y coordinate of body position will be stored.
240 */
241 public void getPositionY(final Distance result) {
242 result.setValue(y);
243 result.setUnit(DistanceUnit.METER);
244 }
245
246 /**
247 * Gets cartesian y coordinate of body position resolved along ECI or ECEF-frame axes.
248 *
249 * @return y coordinate of body position resolved along ECI or ECEF-frame axes.
250 */
251 public Distance getPositionY() {
252 return new Distance(y, DistanceUnit.METER);
253 }
254
255 /**
256 * Sets cartesian y coordinate of body position resolved along ECI or ECEF-frame axes.
257 *
258 * @param positionY cartesian y coordinate of body position to be set.
259 */
260 public void setPositionY(final Distance positionY) {
261 y = DistanceConverter.convert(positionY.getValue().doubleValue(), positionY.getUnit(), DistanceUnit.METER);
262 }
263
264 /**
265 * Gets cartesian z coordinate of body position resolved along ECI or ECEF-frame axes.
266 *
267 * @param result instance where cartesian z coordinate of body position will be stored.
268 */
269 public void getPositionZ(final Distance result) {
270 result.setValue(z);
271 result.setUnit(DistanceUnit.METER);
272 }
273
274 /**
275 * Gets cartesian z coordinate of body position resolved along ECI or ECEF-frame axes.
276 *
277 * @return z coordinate of body position resolved along ECI or ECEF-frame axes.
278 */
279 public Distance getPositionZ() {
280 return new Distance(z, DistanceUnit.METER);
281 }
282
283 /**
284 * Sets cartesian z coordinate of body position resolved along ECI or ECEF-frame axes.
285 *
286 * @param positionZ cartesian z coordinate of body position to be set.
287 */
288 public void setPositionZ(final Distance positionZ) {
289 z = DistanceConverter.convert(positionZ.getValue().doubleValue(), positionZ.getUnit(), DistanceUnit.METER);
290 }
291
292 /**
293 * Sets cartesian coordinates of body position resolved along ECI or ECEF-frame axes.
294 *
295 * @param positionX cartesian x coordinate of body position to be set, resolved along ECI or ECEF-frame axes.
296 * @param positionY cartesian y coordinate of body position to be set, resolved along ECI or ECEF-frame axes.
297 * @param positionZ cartesian z coordinate of body position to be set, resolved along ECI or ECEF-frame axes.
298 */
299 public void setPositionCoordinates(final Distance positionX, final Distance positionY, final Distance positionZ) {
300 setPositionX(positionX);
301 setPositionY(positionY);
302 setPositionZ(positionZ);
303 }
304
305 /**
306 * Gets norm of position expressed in meters (m), which represents the distance to
307 * Earth's center of mass.
308 *
309 * @return position norm expressed in meters (m).
310 */
311 public double getPositionNorm() {
312 return Math.sqrt(x * x + y * y + z * z);
313 }
314
315 /**
316 * Gets norm of position, which represents the distance to Earth's center of mass.
317 *
318 * @param result instance where result will be stored.
319 */
320 public void getPositionNormAsDistance(final Distance result) {
321 result.setValue(getPositionNorm());
322 result.setUnit(DistanceUnit.METER);
323 }
324
325 /**
326 * Gets norm of position, which represents the distance to Earth's center of mass.
327 *
328 * @return position norm.
329 */
330 public Distance getPositionNormAsDistance() {
331 return new Distance(getPositionNorm(), DistanceUnit.METER);
332 }
333
334 /**
335 * Gets x coordinate of velocity of body frame expressed in meters per second (m/s) resolved along ECI or
336 * ECEF-frame axes.
337 *
338 * @return x coordinate of velocity.
339 */
340 public double getVx() {
341 return vx;
342 }
343
344 /**
345 * Sets x coordinate of velocity of body frame expressed in meters per second (m/s) resolved along ECI or
346 * ECEF-frame axes.
347 *
348 * @param vx x coordinate of velocity.
349 */
350 public void setVx(final double vx) {
351 this.vx = vx;
352 }
353
354 /**
355 * Gets y coordinate of velocity of body frame expressed in meters per second (m/s) resolved along ECI or
356 * ECEF-frame axes.
357 *
358 * @return y coordinate of velocity.
359 */
360 public double getVy() {
361 return vy;
362 }
363
364 /**
365 * Sets y coordinate of velocity of body frame expressed in meters per second (m/s) resolved along ECI or
366 * ECEF-frame axes.
367 *
368 * @param vy y coordinate of velocity.
369 */
370 public void setVy(final double vy) {
371 this.vy = vy;
372 }
373
374 /**
375 * Gets z coordinate of velocity of body frame expressed in meters per second (m/s) resolved along ECI or
376 * ECEF-frame axes.
377 *
378 * @return z coordinate of velocity.
379 */
380 public double getVz() {
381 return vz;
382 }
383
384 /**
385 * Sets z coordinate of velocity of body frame expressed in meters per second (m/s) resolved along ECI or
386 * ECEF-frame axes.
387 *
388 * @param vz z coordinate of velocity.
389 */
390 public void setVz(final double vz) {
391 this.vz = vz;
392 }
393
394 /**
395 * Sets velocity coordinates of body frame expressed in meters per second (m/s) resolved along ECI or
396 * ECEF-frame axes.
397 *
398 * @param vx x coordinate of velocity.
399 * @param vy y coordinate of velocity.
400 * @param vz z coordinate of velocity.
401 */
402 public void setVelocityCoordinates(final double vx, final double vy, final double vz) {
403 this.vx = vx;
404 this.vy = vy;
405 this.vz = vz;
406 }
407
408 /**
409 * Gets norm of velocity expressed in meters per second (m/s), which represents
410 * the speed of the body.
411 *
412 * @return norm of velocity expressed in meters per second (m/s).
413 */
414 public double getVelocityNorm() {
415 return Math.sqrt(vx * vx + vy * vy + vz * vz);
416 }
417
418 /**
419 * Gets norm of velocity, which represents the speed of the body.
420 *
421 * @param result velocity norm.
422 */
423 public void getVelocityNormAsSpeed(final Speed result) {
424 result.setValue(getVelocityNorm());
425 result.setUnit(SpeedUnit.METERS_PER_SECOND);
426 }
427
428 /**
429 * Gets norm of velocity, which represents the speed of the body.
430 *
431 * @return velocity norm.
432 */
433 public Speed getVelocityNormAsSpeed() {
434 return new Speed(getVelocityNorm(), SpeedUnit.METERS_PER_SECOND);
435 }
436
437 /**
438 * Gets x coordinate of velocity of body frame resolved along ECEF-frame axes.
439 *
440 * @param result instance where x coordinate of velocity will be stored.
441 */
442 public void getSpeedX(final Speed result) {
443 result.setValue(vx);
444 result.setUnit(SpeedUnit.METERS_PER_SECOND);
445 }
446
447 /**
448 * Gets x coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
449 *
450 * @return x coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
451 */
452 public Speed getSpeedX() {
453 return new Speed(vx, SpeedUnit.METERS_PER_SECOND);
454 }
455
456 /**
457 * Sets x coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
458 *
459 * @param speedX x coordinate of velocity of body frame resolved along ECI or ECEF-frame
460 * axes to be set.
461 */
462 public void setSpeedX(final Speed speedX) {
463 vx = SpeedConverter.convert(speedX.getValue().doubleValue(), speedX.getUnit(), SpeedUnit.METERS_PER_SECOND);
464 }
465
466 /**
467 * Gets y coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
468 *
469 * @param result instance where y coordinate of velocity will be stored.
470 */
471 public void getSpeedY(final Speed result) {
472 result.setValue(vy);
473 result.setUnit(SpeedUnit.METERS_PER_SECOND);
474 }
475
476 /**
477 * Gets y coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
478 *
479 * @return y coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
480 */
481 public Speed getSpeedY() {
482 return new Speed(vy, SpeedUnit.METERS_PER_SECOND);
483 }
484
485 /**
486 * Sets y coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
487 *
488 * @param speedY y coordinate of velocity of body frame resolved along ECI or ECEF-frame
489 * axes to be set.
490 */
491 public void setSpeedY(final Speed speedY) {
492 vy = SpeedConverter.convert(speedY.getValue().doubleValue(), speedY.getUnit(), SpeedUnit.METERS_PER_SECOND);
493 }
494
495 /**
496 * Gets z coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
497 *
498 * @param result instance where z coordinate of velocity will be stored.
499 */
500 public void getSpeedZ(final Speed result) {
501 result.setValue(vz);
502 result.setUnit(SpeedUnit.METERS_PER_SECOND);
503 }
504
505 /**
506 * Gets z coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
507 *
508 * @return z coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
509 */
510 public Speed getSpeedZ() {
511 return new Speed(vz, SpeedUnit.METERS_PER_SECOND);
512 }
513
514 /**
515 * Sets z coordinate of velocity of body frame resolved along ECI or ECEF-frame axes.
516 *
517 * @param speedZ z coordinate of velocity of body frame resolved along ECI or ECEF-frame
518 * axes to be set.
519 */
520 public void setSpeedZ(final Speed speedZ) {
521 vz = SpeedConverter.convert(speedZ.getValue().doubleValue(), speedZ.getUnit(), SpeedUnit.METERS_PER_SECOND);
522 }
523
524 /**
525 * Sets velocity coordinates of body frame resolved along ECI or ECEF-frame axes.
526 *
527 * @param speedX x coordinate of velocity to be set.
528 * @param speedY y coordinate of velocity to be set.
529 * @param speedZ z coordinate of velocity to be set.
530 */
531 public void setSpeedCoordinates(final Speed speedX, final Speed speedY, final Speed speedZ) {
532 setSpeedX(speedX);
533 setSpeedY(speedY);
534 setSpeedZ(speedZ);
535 }
536
537 /**
538 * Gets coordinate transformation matrix.
539 * This is equivalent to calling getCoordinateTransformation(), but more efficient
540 *
541 * @return coordinate transformation matrix.
542 */
543 @Override
544 public Matrix getCoordinateTransformationMatrix() {
545 Matrix result;
546 try {
547 result = new Matrix(CoordinateTransformation.ROWS, CoordinateTransformation.COLS);
548 getCoordinateTransformationMatrix(result);
549 } catch (final WrongSizeException ignore) {
550 // never happens
551 result = null;
552 }
553 return result;
554 }
555
556 /**
557 * Gets coordinate transformation matrix.
558 * This is equivalent to calling getCoordinateTransformation().getMatrix(), but more efficient
559 *
560 * @param result instance where coordinate transformation matrix will be copied to.
561 */
562 @Override
563 public void getCoordinateTransformationMatrix(final Matrix result) {
564 c.matrix.copyTo(result);
565 }
566
567 /**
568 * Sets coordinate transformation matrix keeping current source and destination {@link FrameType}.
569 * This is more efficient than getting a copy of coordinate transformation calling to
570 * {@link #getCoordinateTransformation()}, setting coordinate matrix into copied coordinate transformation and
571 * then setting the coordinate transformation calling {@link #setCoordinateTransformation(CoordinateTransformation)}.
572 *
573 * @param matrix a 3x3 coordinate transformation matrix to be set.
574 * @param threshold threshold to validate rotation matrix.
575 * @throws InvalidRotationMatrixException if provided matrix is not a valid rotation matrix (3x3 and orthonormal).
576 * @throws IllegalArgumentException if provided threshold is negative.
577 */
578 @Override
579 public void setCoordinateTransformationMatrix(final Matrix matrix, final double threshold)
580 throws InvalidRotationMatrixException {
581 c.setMatrix(matrix,threshold);
582 }
583
584 /**
585 * Sts coordinate transformation matrix keeping current source and destination {@link FrameType}.
586 * This is more efficient than getting a copy of coordinate transformation calling to
587 * {@link #getCoordinateTransformation()}, setting coordinate matrix into copied coordinate transformation and
588 * then setting the coordinate transformation calling {@link #setCoordinateTransformation(CoordinateTransformation)}.
589 *
590 * @param matrix a 3x3 coordinate transformation matrix to be set.
591 * @throws InvalidRotationMatrixException if provided matrix is not a valid rotation matrix (3x3 and orthonormal).
592 */
593 @Override
594 public void setCoordinateTransformationMatrix(final Matrix matrix) throws InvalidRotationMatrixException {
595 c.setMatrix(matrix);
596 }
597
598 /**
599 * Gets coordinate transformation as a new 3D rotation instance.
600 * This is equivalent to calling getCoordinateTransformation().asRotation(), but more efficient.
601 *
602 * @return new coordinate transformation as a 3D rotation.
603 * @throws InvalidRotationMatrixException if internal matrix cannot be converted to a 3D rotation.
604 */
605 @Override
606 public Rotation3D getCoordinateTransformationRotation() throws InvalidRotationMatrixException {
607 return c.asRotation();
608 }
609
610 /**
611 * Gets coordinate transformation as a 3D rotation.
612 * This is equivalent to calling getCoordinateTransformation().asRotation(), but more efficient.
613 *
614 * @param result instance where coordinate transformation 3D rotation will be copied to.
615 * @throws InvalidRotationMatrixException if internal matrix cannot be converted to a 3D rotation.
616 */
617 @Override
618 public void getCoordinateTransformationRotation(final Rotation3D result) throws InvalidRotationMatrixException {
619 c.asRotation(result);
620 }
621
622 /**
623 * Sets coordinate transformation from 3D rotation and keeping current source and destination {@link FrameType}.
624 * This is more efficient than getting a copy of coordinate transformation calling to
625 * {@link #getCoordinateTransformation()}, setting rotation into copied coordinate transformation and
626 * then setting the coordinate transformation calling {@link #setCoordinateTransformation(CoordinateTransformation)}.
627 *
628 * @param rotation set rotation into current coordinate rotation.
629 */
630 @Override
631 public void setCoordinateTransformationRotation(final Rotation3D rotation) {
632 c.fromRotation(rotation);
633 }
634
635 /**
636 * Copies this instance data into provided instance.
637 *
638 * @param output destination instance where data will be copied to.
639 */
640 public void copyTo(final T output) {
641 output.x = x;
642 output.y = y;
643 output.z = z;
644
645 output.vx = vx;
646 output.vy = vy;
647 output.vz = vz;
648
649 c.copyTo(output.c);
650 }
651
652 /**
653 * Copies data of provided instance into this instance.
654 *
655 * @param input instance to copy data from.
656 */
657 public void copyFrom(final T input) {
658 x = input.x;
659 y = input.y;
660 z = input.z;
661
662 vx = input.vx;
663 vy = input.vy;
664 vz = input.vz;
665
666 c.copyFrom(input.c);
667 }
668
669 /**
670 * Computes and returns hash code for this instance. Hash codes are almost unique
671 * values that are useful for fast classification and storage of objects in collections.
672 *
673 * @return Hash code.
674 */
675 @Override
676 public int hashCode() {
677 return Objects.hash(x, y, z, vx, vy, vz, c);
678 }
679
680 /**
681 * Checks if provided instance has exactly the same contents as this instance.
682 *
683 * @param obj instance to be compared.
684 * @return true if both instances are considered to be equal, false otherwise.
685 */
686 @Override
687 public boolean equals(final Object obj) {
688 if (obj == null) {
689 return false;
690 }
691 if (obj == this) {
692 return true;
693 }
694 if (!clazz.isInstance(obj)) {
695 return false;
696 }
697
698 //noinspection unchecked
699 final T other = (T) obj;
700 return equals(other, 0.0);
701 }
702
703 /**
704 * Checks if provided instance has contents similar to this instance up to provided
705 * threshold value.
706 *
707 * @param other instance to be compared.
708 * @param threshold maximum difference allowed between position, velocity and coordinate transformation matrix.
709 * @return true if both instances are considered to be equal (up to provided threshold), false otherwise.
710 */
711 public boolean equals(final T other, final double threshold) {
712 if (other == null) {
713 return false;
714 }
715
716 return Math.abs(x - other.x) <= threshold
717 && Math.abs(y - other.y) <= threshold
718 && Math.abs(z - other.z) <= threshold
719 && Math.abs(vx - other.vx) <= threshold
720 && Math.abs(vy - other.vy) <= threshold
721 && Math.abs(vz - other.vz) <= threshold
722 && c.equals(other.c, threshold);
723 }
724 }