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.FrameType;
24  import com.irurueta.navigation.frames.InvalidSourceAndDestinationFrameTypeException;
25  import com.irurueta.navigation.frames.NEDFrame;
26  import com.irurueta.navigation.geodesic.Constants;
27  
28  /**
29   * Converts from NED frame to ECEF frame.
30   * This implementation is based on the equations defined in "Principles of GNSS, Inertial, and Multi-sensor
31   * Integrated Navigation Systems, Second Edition" and on the companion software available at:
32   * <a href="https://github.com/ymjdz/MATLAB-Codes/blob/master/NED_to_ECEF.m">
33   *     https://github.com/ymjdz/MATLAB-Codes/blob/master/NED_to_ECEF.m
34   * </a>
35   */
36  public class NEDtoECEFFrameConverter implements FrameConverter<NEDFrame, ECEFFrame> {
37  
38      /**
39       * The equatorial radius of WGS84 ellipsoid (6378137 m) defining Earth's shape.
40       */
41      public static final double EARTH_EQUATORIAL_RADIUS_WGS84 = Constants.EARTH_EQUATORIAL_RADIUS_WGS84;
42  
43      /**
44       * Earth eccentricity as defined on the WGS84 ellipsoid.
45       */
46      public static final double EARTH_ECCENTRICITY = Constants.EARTH_ECCENTRICITY;
47  
48      /**
49       * Converts source NED frame to a new ECEF frame instance.
50       *
51       * @param source source frame to convert from.
52       * @return a new destination frame instance.
53       */
54      @Override
55      public ECEFFrame convertAndReturnNew(final NEDFrame source) {
56          final var result = new ECEFFrame();
57          convert(source, result);
58          return result;
59      }
60  
61      /**
62       * Converts source NED frame to destination ECEF frame.
63       * @param source      source frame to convert from.
64       * @param destination destination frame instance to convert to.
65       */
66      @Override
67      public void convert(final NEDFrame source, final ECEFFrame destination) {
68          convertNEDtoECEF(source, destination);
69      }
70  
71      /**
72       * Gets source frame type.
73       *
74       * @return source frame type.
75       */
76      @Override
77      public FrameType getSourceType() {
78          return FrameType.LOCAL_NAVIGATION_FRAME;
79      }
80  
81      /**
82       * Gets destination frame type.
83       *
84       * @return destination frame type.
85       */
86      @Override
87      public FrameType getDestinationType() {
88          return FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME;
89      }
90  
91      /**
92       * Converts source NED frame to a new ECEF frame instance.
93       *
94       * @param source source frame to convert from.
95       * @return a new destination frame instance.
96       */
97      public static ECEFFrame convertNEDtoECEFAndReturnNew(final NEDFrame source) {
98          final var result = new ECEFFrame();
99          convertNEDtoECEF(source, result);
100         return result;
101     }
102 
103     /**
104      * Converts source NED frame to destination ECEF frame.
105      * @param source      source frame to convert from.
106      * @param destination destination frame instance to convert to.
107      */
108     @SuppressWarnings("DuplicatedCode")
109     public static void convertNEDtoECEF(final NEDFrame source, final ECEFFrame destination) {
110         try {
111             final var latitude = source.getLatitude();
112             final var longitude = source.getLongitude();
113             final var height = source.getHeight();
114 
115             final var cosLat = Math.cos(latitude);
116             final var sinLat = Math.sin(latitude);
117             final var cosLong = Math.cos(longitude);
118             final var sinLong = Math.sin(longitude);
119 
120             // Calculate transverse radius of curvature using (2.105)
121             final var eSinLat = EARTH_ECCENTRICITY * sinLat;
122             final var eSinLat2 = eSinLat * eSinLat;
123             var re = EARTH_EQUATORIAL_RADIUS_WGS84 / Math.sqrt(1.0 - eSinLat2);
124 
125             // Convert position using (2.112)
126             final var e2 = EARTH_ECCENTRICITY * EARTH_ECCENTRICITY;
127             final var x = (re + height) * cosLat * cosLong;
128             final var y = (re + height) * cosLat * sinLong;
129             final var z = ((1.0 - e2) * re + height) * sinLat;
130 
131             // Calculate NED to ECEF coordinate transformation matrix
132             final var cne = CoordinateTransformation.nedToEcefMatrix(latitude, longitude);
133 
134             // Transform velocity using (2.73)
135             final var vn = source.getVn();
136             final var ve = source.getVe();
137             final var vd = source.getVd();
138             final var vEbn = new Matrix(NEDFrame.NUM_VELOCITY_COORDINATES, 1);
139             vEbn.setElementAtIndex(0, vn);
140             vEbn.setElementAtIndex(1, ve);
141             vEbn.setElementAtIndex(2, vd);
142 
143             final var vEbe = cne.multiplyAndReturnNew(vEbn);
144             final var vx = vEbe.getElementAtIndex(0);
145             final var vy = vEbe.getElementAtIndex(1);
146             final var vz = vEbe.getElementAtIndex(2);
147 
148             // Transform attitude using (2.15)
149             final var cbn = source.getCoordinateTransformation().getMatrix();
150             cne.multiply(cbn); // cne is now cbe
151 
152             final var c = new CoordinateTransformation(cne, FrameType.BODY_FRAME,
153                     FrameType.EARTH_CENTERED_EARTH_FIXED_FRAME);
154 
155             // set result
156             destination.setX(x);
157             destination.setY(y);
158             destination.setZ(z);
159 
160             destination.setVx(vx);
161             destination.setVy(vy);
162             destination.setVz(vz);
163 
164             destination.setCoordinateTransformation(c);
165 
166         } catch (final WrongSizeException | InvalidRotationMatrixException |
167                 InvalidSourceAndDestinationFrameTypeException ignore) {
168             // never happens
169         }
170     }
171 }