View Javadoc
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.converters;
17  
18  import com.irurueta.algebra.Matrix;
19  import com.irurueta.algebra.WrongSizeException;
20  import com.irurueta.geometry.InvalidRotationMatrixException;
21  import com.irurueta.navigation.frames.CoordinateTransformation;
22  import com.irurueta.navigation.frames.ECEFFrame;
23  import com.irurueta.navigation.frames.ECIFrame;
24  import com.irurueta.navigation.frames.ECIorECEFFrame;
25  import com.irurueta.navigation.frames.FrameType;
26  import com.irurueta.navigation.frames.InvalidSourceAndDestinationFrameTypeException;
27  import com.irurueta.navigation.geodesic.Constants;
28  import com.irurueta.units.Time;
29  import com.irurueta.units.TimeConverter;
30  import com.irurueta.units.TimeUnit;
31  
32  /**
33   * Converts from ECI frame to ECEF frame.
34   * This implementation is based on the equations defined in "Principles of GNSS, Inertial, and Multi-sensor
35   * Integrated Navigation Systems, Second Edition" and on the companion software available at:
36   * <a href="https://github.com/ymjdz/MATLAB-Codes/blob/master/ECI_to_ECEF.m">
37   *     https://github.com/ymjdz/MATLAB-Codes/blob/master/ECI_to_ECEF.m
38   * </a>
39   */
40  @SuppressWarnings("WeakerAccess")
41  public class ECItoECEFFrameConverter implements TimeIntervalFrameConverter<ECIFrame, ECEFFrame> {
42  
43      /**
44       * Earth rotation rate expressed in radians per second (rad/s).
45       */
46      public static final double EARTH_ROTATION_RATE = Constants.EARTH_ROTATION_RATE;
47  
48      /**
49       * Converts source ECI frame to a new ECEF frame instance.
50       *
51       * @param timeInterval a time interval expressed in seconds (s).
52       * @param source       source frame to convert from.
53       * @return a new destination frame instance.
54       */
55      @Override
56      public ECEFFrame convertAndReturnNew(final double timeInterval, final ECIFrame source) {
57          return convertECItoECEFAndReturnNew(timeInterval, source);
58      }
59  
60      /**
61       * Converts source frame to a new destination frame instance.
62       *
63       * @param timeInterval a time interval.
64       * @param source       source frame to convert from.
65       * @return a new destination frame instance.
66       */
67      @Override
68      public ECEFFrame convertAndReturnNew(final Time timeInterval, final ECIFrame source) {
69          return convertECItoECEFAndReturnNew(timeInterval, source);
70      }
71  
72      /**
73       * Converts source ECI frame to destination ECEF frame.
74       *
75       * @param timeInterval a time interval expressed in seconds (s).
76       * @param source       source frame to convert from.
77       * @param destination  destination frame instance to convert to.
78       */
79      @Override
80      public void convert(final double timeInterval, final ECIFrame source, final ECEFFrame destination) {
81          convertECItoECEF(timeInterval, source, destination);
82      }
83  
84      /**
85       * Converts source frame to destination frame.
86       *
87       * @param timeInterval a time interval.
88       * @param source       source frame to convert from.
89       * @param destination  destination frame instance to covert to.
90       */
91      @Override
92      public void convert(final Time timeInterval, final ECIFrame source, final ECEFFrame destination) {
93          convertECItoECEF(timeInterval, source, destination);
94      }
95  
96      /**
97       * Gets source frame type.
98       *
99       * @return source frame type.
100      */
101     @Override
102     public FrameType getSourceType() {
103         return FrameType.EARTH_CENTERED_INERTIAL_FRAME;
104     }
105 
106     /**
107      * Gets destination frame type.
108      *
109      * @return destination frame type.
110      */
111     @Override
112     public FrameType getDestinationType() {
113         return FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME;
114     }
115 
116     /**
117      * Converts source ECI frame to a new ECEF frame instance.
118      *
119      * @param timeInterval a time interval expressed in seconds (s).
120      * @param source       source frame to convert from.
121      * @return a new destination frame instance.
122      */
123     public static ECEFFrame convertECItoECEFAndReturnNew(final double timeInterval, final ECIFrame source) {
124         final var result = new ECEFFrame();
125         convertECItoECEF(timeInterval, source, result);
126         return result;
127     }
128 
129     /**
130      * Converts source ECI frame to a new ECEF frame instance.
131      *
132      * @param timeInterval a time interval.
133      * @param source       source frame to convert from.
134      * @return a new destination frame instance.
135      */
136     public static ECEFFrame convertECItoECEFAndReturnNew(final Time timeInterval, final ECIFrame source) {
137         return convertECItoECEFAndReturnNew(TimeConverter.convert(timeInterval.getValue().doubleValue(),
138                 timeInterval.getUnit(), TimeUnit.SECOND), source);
139     }
140 
141     /**
142      * Converts source ECI frame to destination ECEF frame.
143      *
144      * @param timeInterval a time interval expressed in seconds (s).
145      * @param source       source frame to convert from.
146      * @param destination  destination frame instance to convert to.
147      */
148     @SuppressWarnings("DuplicatedCode")
149     public static void convertECItoECEF(final double timeInterval, final ECIFrame source, final ECEFFrame destination) {
150         try {
151             // Calculate ECEF to ECI coordinate transformation matrix using (2.145)
152             final var alpha = EARTH_ROTATION_RATE * timeInterval;
153             final var cie = CoordinateTransformation.eciToEcefMatrixFromAngle(alpha);
154 
155             // Transform position using (2.146)
156             final var rIbi = new Matrix(ECIorECEFFrame.NUM_POSITION_COORDINATES, 1);
157             rIbi.setElementAtIndex(0, source.getX());
158             rIbi.setElementAtIndex(1, source.getY());
159             rIbi.setElementAtIndex(2, source.getZ());
160 
161             final var rEbe = cie.multiplyAndReturnNew(rIbi);
162 
163             destination.setCoordinates(rEbe.getElementAtIndex(0), rEbe.getElementAtIndex(1), rEbe.getElementAtIndex(2));
164 
165             // Transform velocity using (2.145)
166             final var tmp = new Matrix(ECIorECEFFrame.NUM_POSITION_COORDINATES, 1);
167             tmp.setElementAtIndex(0, -source.getY());
168             tmp.setElementAtIndex(1, source.getX());
169             tmp.setElementAtIndex(2, 0.0);
170             tmp.multiplyByScalar(-EARTH_ROTATION_RATE);
171 
172             final var vIbi = new Matrix(ECIorECEFFrame.NUM_VELOCITY_COORDINATES, 1);
173             vIbi.setElementAtIndex(0, source.getVx());
174             vIbi.setElementAtIndex(1, source.getVy());
175             vIbi.setElementAtIndex(2, source.getVz());
176 
177             // vIbi - omega * [-y;x;0]
178             vIbi.add(tmp);
179 
180             final var vEbe = cie.multiplyAndReturnNew(vIbi);
181 
182             destination.setVelocityCoordinates(vEbe.getElementAtIndex(0), vEbe.getElementAtIndex(1),
183                     vEbe.getElementAtIndex(2));
184 
185             // Transform attitude using (2.15)
186             // cbe = cie * cbi
187             final var cbi = source.getCoordinateTransformationMatrix();
188             cie.multiply(cbi);
189 
190             final var c = new CoordinateTransformation(cie, FrameType.BODY_FRAME,
191                     FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
192             destination.setCoordinateTransformation(c);
193         } catch (final WrongSizeException | InvalidSourceAndDestinationFrameTypeException |
194                 InvalidRotationMatrixException ignore) {
195             // never happens
196         }
197     }
198 
199     /**
200      * Converts source ECI frame to destination ECEF frame.
201      *
202      * @param timeInterval a time interval.
203      * @param source       source frame to convert from.
204      * @param destination  destination frame instance to convert to.
205      */
206     public static void convertECItoECEF(final Time timeInterval, final ECIFrame source,
207                                         final ECEFFrame destination) {
208         convertECItoECEF(TimeConverter.convert(timeInterval.getValue().doubleValue(), timeInterval.getUnit(),
209                 TimeUnit.SECOND), source, destination);
210     }
211 }