IodGibbs.java

  1. /* Copyright 2002-2025 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. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  19. import org.hipparchus.util.FastMath;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.estimation.measurements.PV;
  23. import org.orekit.estimation.measurements.Position;
  24. import org.orekit.frames.Frame;
  25. import org.orekit.orbits.Orbit;
  26. import org.orekit.orbits.CartesianOrbit;
  27. import org.orekit.time.AbsoluteDate;
  28. import org.orekit.utils.PVCoordinates;

  29. /**
  30.  * Gibbs position-based Initial Orbit Determination (IOD) algorithm.
  31.  * <p>
  32.  * An orbit is determined from three position vectors. This method requires
  33.  * the vectors to be coplanar. Orekit uses a {@link IodGibbs#COPLANAR_THRESHOLD
  34.  * default coplanar threshold of 5°}.
  35.  *
  36.  * Reference:
  37.  *  Vallado, D., Fundamentals of Astrodynamics and Applications
  38.  * </p>
  39.  * @author Joris Olympio
  40.  * @since 8.0
  41.  */
  42. public class IodGibbs {

  43.     /** Gravitational constant. **/
  44.     private final double mu;

  45.     /** Threshold for checking coplanar vectors. */
  46.     private final double COPLANAR_THRESHOLD = FastMath.toRadians(5.);

  47.     /** Creator.
  48.      *
  49.      * @param mu gravitational constant
  50.      */
  51.     public IodGibbs(final double mu) {
  52.         this.mu = mu;
  53.     }

  54.     /** Give an initial orbit estimation, assuming Keplerian motion.
  55.      * All observations should be from the same location.
  56.      *
  57.      * @param frame measurements frame
  58.      * @param p1 First position measurement
  59.      * @param p2 Second position measurement
  60.      * @param p3 Third position measurement
  61.      * @return an initial orbit estimation at the central date
  62.      *         (i.e., date of the second position measurement)
  63.      * @since 11.0
  64.      */
  65.     public Orbit estimate(final Frame frame, final Position p1, final Position p2, final Position p3) {
  66.         return estimate(frame,
  67.                         p1.getPosition(), p1.getDate(),
  68.                         p2.getPosition(), p2.getDate(),
  69.                         p3.getPosition(), p3.getDate());
  70.     }

  71.     /** Give an initial orbit estimation, assuming Keplerian motion.
  72.      * All observations should be from the same location.
  73.      *
  74.      * @param frame measure frame
  75.      * @param pv1 PV measure 1 taken in frame
  76.      * @param pv2 PV measure 2 taken in frame
  77.      * @param pv3 PV measure 3 taken in frame
  78.      * @return an initial orbit estimation at the central date
  79.      *         (i.e., date of the second PV measurement)
  80.      */
  81.     public Orbit estimate(final Frame frame, final PV pv1, final PV pv2, final PV pv3) {
  82.         return estimate(frame,
  83.                         pv1.getPosition(), pv1.getDate(),
  84.                         pv2.getPosition(), pv2.getDate(),
  85.                         pv3.getPosition(), pv3.getDate());
  86.     }

  87.     /** Give an initial orbit estimation, assuming Keplerian motion.
  88.      * All observations should be from the same location.
  89.      *
  90.      * @param frame measure frame
  91.      * @param r1 position 1 measured in frame
  92.      * @param date1 date of measure 1
  93.      * @param r2 position 2 measured in frame
  94.      * @param date2 date of measure 2
  95.      * @param r3 position 3 measured in frame
  96.      * @param date3 date of measure 3
  97.      * @return an initial orbit estimation at the central date
  98.      *         (i.e., date of the second position measurement)
  99.      */
  100.     public Orbit estimate(final Frame frame,
  101.                           final Vector3D r1, final AbsoluteDate date1,
  102.                           final Vector3D r2, final AbsoluteDate date2,
  103.                           final Vector3D r3, final AbsoluteDate date3) {
  104.         // Checks measures are not at the same date
  105.         if (date1.equals(date2) || date1.equals(date3) || date2.equals(date3)) {
  106.             throw new OrekitException(OrekitMessages.NON_DIFFERENT_DATES_FOR_OBSERVATIONS, date1, date2, date3,
  107.                     date2.durationFrom(date1), date3.durationFrom(date1), date3.durationFrom(date2));
  108.         }

  109.         // Checks measures are in the same plane
  110.         final double num = r1.normalize().dotProduct(r2.normalize().crossProduct(r3.normalize()));
  111.         final double alpha = FastMath.PI / 2.0 - FastMath.acos(num);
  112.         if (FastMath.abs(alpha) > COPLANAR_THRESHOLD) {
  113.             throw new OrekitException(OrekitMessages.NON_COPLANAR_POINTS);
  114.         }

  115.         final Vector3D D = r1.crossProduct(r2).add(r2.crossProduct(r3).add(r3.crossProduct(r1)));

  116.         final Vector3D N = (r2.crossProduct(r3)).scalarMultiply(r1.getNorm())
  117.                 .add((r3.crossProduct(r1)).scalarMultiply(r2.getNorm()))
  118.                 .add((r1.crossProduct(r2)).scalarMultiply(r3.getNorm()));

  119.         final Vector3D B = D.crossProduct(r2);

  120.         final Vector3D S = r1.scalarMultiply(r2.getNorm() - r3.getNorm())
  121.                 .add(r2.scalarMultiply(r3.getNorm() - r1.getNorm())
  122.                      .add(r3.scalarMultiply(r1.getNorm() - r2.getNorm())));

  123.         // middle velocity
  124.         final double   vm    = FastMath.sqrt(mu / (N.getNorm() * D.getNorm()));
  125.         final Vector3D vlEci = B.scalarMultiply(vm / r2.getNorm()).add(S.scalarMultiply(vm));

  126.         // compile a new middle point with position, velocity
  127.         final PVCoordinates pv   = new PVCoordinates(r2, vlEci);

  128.         // compute the equivalent Cartesian orbit
  129.         final CartesianOrbit orbit = new CartesianOrbit(pv, frame, date2, mu);

  130.         //define the reverse orbit
  131.         final PVCoordinates pv2 = new PVCoordinates(r2, vlEci.scalarMultiply(-1));
  132.         final CartesianOrbit orbit2 = new CartesianOrbit(pv2, frame, date2, mu);

  133.         //check which orbit is correct
  134.         final Vector3D estP3 = orbit.shiftedBy(date3.durationFrom(date2)).
  135.                                     getPosition();
  136.         final double dist = estP3.subtract(r3).getNorm();
  137.         final Vector3D estP3_2 = orbit2.shiftedBy(date3.durationFrom(date2)).
  138.                                        getPosition();
  139.         final double dist2 = estP3_2.subtract(r3).getNorm();

  140.         if (dist <= dist2) {
  141.             return orbit;
  142.         } else {
  143.             return orbit2;
  144.         }
  145.     }
  146. }