1   /* Copyright 2002-2021 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.estimation.iod;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.FastMath;
21  import org.orekit.errors.OrekitException;
22  import org.orekit.errors.OrekitMessages;
23  import org.orekit.estimation.measurements.PV;
24  import org.orekit.estimation.measurements.Position;
25  import org.orekit.frames.Frame;
26  import org.orekit.orbits.KeplerianOrbit;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.utils.PVCoordinates;
29  
30  /**
31   * Gibbs initial orbit determination.
32   * An orbit is determined from three position vectors.
33   *
34   * Reference:
35   *  Vallado, D., Fundamentals of Astrodynamics and Applications
36   *
37   * @author Joris Olympio
38   * @since 8.0
39   *
40   */
41  public class IodGibbs {
42  
43      /** Threshold for checking coplanr vectors. */
44      private static final double COPLANAR_THRESHOLD = FastMath.toRadians(5.);
45  
46      /** gravitationnal constant. */
47      private final double mu;
48  
49      /** Creator.
50       *
51       * @param mu gravitational constant
52       */
53      public IodGibbs(final double mu) {
54          this.mu = mu;
55      }
56  
57      /** Give an initial orbit estimation, assuming Keplerian motion.
58       * All observations should be from the same location.
59       *
60       * @param frame measurements frame
61       * @param p1 First position measurement
62       * @param p2 Second position measurement
63       * @param p3 Third position measurement
64       * @return an initial orbit estimation
65       * @since 11.0
66       */
67      public KeplerianOrbit estimate(final Frame frame, final Position p1, final Position p2, final Position p3) {
68          return estimate(frame,
69                          p1.getPosition(), p1.getDate(),
70                          p2.getPosition(), p2.getDate(),
71                          p3.getPosition(), p3.getDate());
72      }
73  
74      /** Give an initial orbit estimation, assuming Keplerian motion.
75       * All observations should be from the same location.
76       *
77       * @param frame measure frame
78       * @param pv1 PV measure 1 taken in frame
79       * @param pv2 PV measure 2 taken in frame
80       * @param pv3 PV measure 3 taken in frame
81       * @return an initial orbit estimation
82       */
83      public KeplerianOrbit estimate(final Frame frame, final PV pv1, final PV pv2, final PV pv3) {
84          return estimate(frame,
85                          pv1.getPosition(), pv1.getDate(),
86                          pv2.getPosition(), pv2.getDate(),
87                          pv3.getPosition(), pv3.getDate());
88      }
89  
90      /** Give an initial orbit estimation, assuming Keplerian motion.
91       * All observations should be from the same location.
92       *
93       * @param frame measure frame
94       * @param r1 position 1 measured in frame
95       * @param date1 date of measure 1
96       * @param r2 position 2 measured in frame
97       * @param date2 date of measure 2
98       * @param r3 position 3 measured in frame
99       * @param date3 date of measure 3
100      * @return an initial orbit estimation
101      */
102     public KeplerianOrbit estimate(final Frame frame,
103                                    final Vector3D r1, final AbsoluteDate date1,
104                                    final Vector3D r2, final AbsoluteDate date2,
105                                    final Vector3D r3, final AbsoluteDate date3) {
106         // Checks measures are not at the same date
107         if (date1.equals(date2) || date1.equals(date3) || date2.equals(date3)) {
108             throw new OrekitException(OrekitMessages.NON_DIFFERENT_DATES_FOR_OBSERVATIONS, date1, date2, date3,
109                     date2.durationFrom(date1), date3.durationFrom(date1), date3.durationFrom(date2));
110         }
111 
112         // Checks measures are in the same plane
113         final double num = r1.normalize().dotProduct(r2.normalize().crossProduct(r3.normalize()));
114         final double alpha = FastMath.PI / 2.0 - FastMath.acos(num);
115         if (FastMath.abs(alpha) > COPLANAR_THRESHOLD) {
116             throw new OrekitException(OrekitMessages.NON_COPLANAR_POINTS);
117         }
118 
119         final Vector3D D = r1.crossProduct(r2).add(r2.crossProduct(r3).add(r3.crossProduct(r1)));
120 
121         final Vector3D N = (r2.crossProduct(r3)).scalarMultiply(r1.getNorm())
122                 .add((r3.crossProduct(r1)).scalarMultiply(r2.getNorm()))
123                 .add((r1.crossProduct(r2)).scalarMultiply(r3.getNorm()));
124 
125         final Vector3D B = D.crossProduct(r2);
126 
127         final Vector3D S = r1.scalarMultiply(r2.getNorm() - r3.getNorm())
128                 .add(r2.scalarMultiply(r3.getNorm() - r1.getNorm())
129                      .add(r3.scalarMultiply(r1.getNorm() - r2.getNorm())));
130 
131         // middle velocity
132         final double vm = FastMath.sqrt(mu / (N.getNorm() * D.getNorm()));
133         final Vector3D vlEci = B.scalarMultiply(vm / r2.getNorm()).add(S.scalarMultiply(vm));
134 
135         // compile a new middle point with position, velocity
136         final PVCoordinates pv = new PVCoordinates(r2, vlEci);
137         final AbsoluteDate date = date2;
138 
139         // compute the equivalent Keplerian orbit
140         final KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, date, mu);
141 
142         //define the reverse orbit
143         final PVCoordinates pv2 = new PVCoordinates(r2, vlEci.scalarMultiply(-1));
144         final KeplerianOrbit orbit2 = new KeplerianOrbit(pv2, frame, date, mu);
145 
146         //check which orbit is correct
147         final Vector3D estP3 = orbit.shiftedBy(date3.durationFrom(date2)).
148                 getPVCoordinates().getPosition();
149         final double dist = estP3.subtract(r3).getNorm();
150         final Vector3D estP3_2 = orbit2.shiftedBy(date3.durationFrom(date2)).
151                 getPVCoordinates().getPosition();
152         final double dist2 = estP3_2.subtract(r3).getNorm();
153 
154         if (dist <= dist2) {
155             return orbit;
156         } else {
157             return orbit2;
158         }
159     }
160 }