IodGauss.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.analysis.solvers.LaguerreSolver;
  19. import org.hipparchus.complex.Complex;
  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.linear.Array2DRowRealMatrix;
  22. import org.hipparchus.linear.LUDecomposition;
  23. import org.hipparchus.linear.RealMatrix;
  24. import org.hipparchus.linear.RealVector;
  25. import org.hipparchus.util.FastMath;
  26. import org.orekit.estimation.measurements.AngularAzEl;
  27. import org.orekit.estimation.measurements.AngularRaDec;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.orbits.CartesianOrbit;
  30. import org.orekit.orbits.Orbit;
  31. import org.orekit.time.AbsoluteDate;
  32. import org.orekit.utils.PVCoordinates;

  33. /**
  34.  * Gauss angles-only Initial Orbit Determination (IOD) algorithm.
  35.  * <p>
  36.  * The algorithm works best when the separation between observation is less than about 60°.
  37.  * The method performs remarkably well when the data is separated by 10° or less.
  38.  *
  39.  * An orbit is determined from three lines of sight w.r.t. their respective observers
  40.  * inertial positions vectors.
  41.  * </p><p>
  42.  * References:
  43.  *   Vallado, D., Fundamentals of Astrodynamics and Applications
  44.  *   Curtis, Orbital Mechanics for Engineering Students
  45.  * </p>
  46.  * @author Julien Asquier
  47.  * @since 12.0
  48.  */
  49. public class IodGauss {

  50.     /** Gravitational constant. */
  51.     private final double mu;

  52.     /**
  53.      * Constructor.
  54.      *
  55.      * @param mu gravitational constant
  56.      */
  57.     public IodGauss(final double mu) {
  58.         this.mu = mu;
  59.     }

  60.     /**
  61.      * Estimate and orbit based on Gauss Intial Orbit Determination method.
  62.      *
  63.      * @param outputFrame inertial frame for observer coordinates and orbit estimate
  64.      * @param azEl1 first angular observation
  65.      * @param azEl2 second angular observation
  66.      * @param azEl3 third angular observation
  67.      * @return an estimate of the orbit at the central date or null if
  68.      *         no estimate is possible with the given data
  69.      */
  70.     public Orbit estimate(final Frame outputFrame, final AngularAzEl azEl1,
  71.                           final AngularAzEl azEl2, final AngularAzEl azEl3) {
  72.         return estimate(outputFrame,
  73.                         azEl1.getGroundStationPosition(outputFrame), azEl1.getDate(), azEl1.getObservedLineOfSight(outputFrame),
  74.                         azEl2.getGroundStationPosition(outputFrame), azEl2.getDate(), azEl2.getObservedLineOfSight(outputFrame),
  75.                         azEl3.getGroundStationPosition(outputFrame), azEl3.getDate(), azEl3.getObservedLineOfSight(outputFrame));
  76.     }

  77.     /**
  78.      * Estimate and orbit based on Gauss Intial Orbit Determination method.
  79.      *
  80.      * @param outputFrame inertial frame for observer coordinates and orbit estimate
  81.      * @param raDec1 first angular observation
  82.      * @param raDec2 second angular observation
  83.      * @param raDec3 third angular observation
  84.      * @return an estimate of the orbit at the central date or null if
  85.      *         no estimate is possible with the given data
  86.      */
  87.     public Orbit estimate(final Frame outputFrame, final AngularRaDec raDec1,
  88.                           final AngularRaDec raDec2, final AngularRaDec raDec3) {
  89.         return estimate(outputFrame,
  90.                         raDec1.getGroundStationPosition(outputFrame), raDec1.getDate(), raDec1.getObservedLineOfSight(outputFrame),
  91.                         raDec2.getGroundStationPosition(outputFrame), raDec2.getDate(), raDec2.getObservedLineOfSight(outputFrame),
  92.                         raDec3.getGroundStationPosition(outputFrame), raDec3.getDate(), raDec3.getObservedLineOfSight(outputFrame));
  93.     }

  94.     /**
  95.      * Estimate and orbit based on Gauss Intial Orbit Determination method.
  96.      *
  97.      * @param outputFrame inertial frame for observer coordinates and orbit estimate
  98.      * @param obsP1 observer position at obsDate1
  99.      * @param obsDate1 date of the 1st observation
  100.      * @param los1 line of sight unit vector at obsDate1
  101.      * @param obsP2 observer position at obsDate2
  102.      * @param obsDate2 date of the 2nd observation
  103.      * @param los2 line of sight unit vector at obsDate2
  104.      * @param obsP3 observer position at obsDate3
  105.      * @param obsDate3 date of the 3rd observation
  106.      * @param los3 line of sight unit vector at obsDate3
  107.      * @return an estimate of the orbit at the central date obsDate2 or null if
  108.      *         no estimate is possible with the given data
  109.      */
  110.     public Orbit estimate(final Frame outputFrame,
  111.                           final Vector3D obsP1, final AbsoluteDate obsDate1, final Vector3D los1,
  112.                           final Vector3D obsP2, final AbsoluteDate obsDate2, final Vector3D los2,
  113.                           final Vector3D obsP3, final AbsoluteDate obsDate3, final Vector3D los3) {

  114.         // Getting the difference of time between 1st and 3rd observation with 2nd observation
  115.         final double tau1 = obsDate1.getDate().durationFrom(obsDate2.getDate());
  116.         final double tau3 = obsDate3.getDate().durationFrom(obsDate2.getDate());

  117.         final double diffTau3Tau1 = tau3 - tau1;

  118.         // mathematical coefficients, see Vallado 7.3.2
  119.         final double a1  = tau3 / diffTau3Tau1;
  120.         final double a3  = -tau1 / diffTau3Tau1;
  121.         final double a1u = tau3 * ((diffTau3Tau1 * diffTau3Tau1) - tau3 * tau3) / (6. * diffTau3Tau1);
  122.         final double a3u = -tau1 * ((diffTau3Tau1 * diffTau3Tau1) - tau1 * tau1) / (6. * diffTau3Tau1);

  123.         // Creating the line of Sight Matrix and inverting it
  124.         final RealMatrix losMatrix = new Array2DRowRealMatrix(3, 3);
  125.         losMatrix.setColumn(0, los1.toArray());
  126.         losMatrix.setColumn(1, los2.toArray());
  127.         losMatrix.setColumn(2, los3.toArray());

  128.         final RealMatrix invertedLosMatrix = new LUDecomposition(losMatrix).getSolver().getInverse();

  129.         // Creating the position of observations matrix
  130.         final RealMatrix rSite = new Array2DRowRealMatrix(3, 3);
  131.         rSite.setColumn(0, obsP1.toArray());
  132.         rSite.setColumn(1, obsP2.toArray());
  133.         rSite.setColumn(2, obsP3.toArray());

  134.         // mathematical matrix and coefficients, see Vallado 7.3.2
  135.         final RealMatrix m = invertedLosMatrix.multiply(rSite);

  136.         final double d1 = m.getEntry(1, 0) * a1 - m.getEntry(1, 1) + m.getEntry(1, 2) * a3;
  137.         final double d2 = m.getEntry(1, 0) * a1u + m.getEntry(1, 2) * a3u;
  138.         final double C  = los2.dotProduct(obsP2);

  139.         // norm of the position of the second observation
  140.         final double r2Norm = obsP2.getNorm();

  141.         // Coefficients of the 8th order polynomial we need to solve to determine "r"
  142.         final double[] coeff = new double[] { -mu * mu * d2 * d2, 0., 0., -2. * mu * (C * d2 + d1 * d2), 0.,
  143.                                               0., -(d1 * d1 + 2. * C * d1 + r2Norm * r2Norm), 0., 1.0 };
  144.         final LaguerreSolver solver = new LaguerreSolver(1E-10,
  145.                                                          1E-10, 1E-10);
  146.         final Complex[] roots = solver.solveAllComplex(coeff, 5. * r2Norm);

  147.         // Looking for the first adequate root of the equation (7-16) of Vallado
  148.         double r2Mag = 0.0;
  149.         for (final Complex root : roots) {
  150.             if (root.getReal() > r2Mag &&
  151.                     FastMath.abs(root.getImaginary()) < solver.getAbsoluteAccuracy()) {
  152.                 r2Mag = root.getReal();
  153.             }
  154.         }
  155.         if (r2Mag == 0.0) {
  156.             return null;
  157.         }

  158.         // mathematical matrix and coefficients, see Vallado 7.3.2
  159.         final double u = mu / (r2Mag * r2Mag * r2Mag);

  160.         final double c1 = -(-a1 - a1u * u);
  161.         final double c2 = -1;
  162.         final double c3 = -(-a3 - a3u * u);

  163.         final RealMatrix cCoeffMatrix = new Array2DRowRealMatrix(3, 1);
  164.         cCoeffMatrix.setEntry(0, 0, -c1);
  165.         cCoeffMatrix.setEntry(1, 0, -c2);
  166.         cCoeffMatrix.setEntry(2, 0, -c3);

  167.         final RealMatrix B = m.multiply(cCoeffMatrix.scalarMultiply(1));

  168.         final RealMatrix A = new Array2DRowRealMatrix(3, 3);
  169.         A.setEntry(0, 0, c1);
  170.         A.setEntry(1, 1, c2);
  171.         A.setEntry(2, 2, c3);

  172.         // Slant ranges matrix
  173.         final RealMatrix slantRanges = new LUDecomposition(A).getSolver().solve(B);

  174.         // Position Matrix of the satellite corresponding to the 1st, 2nd and 3rd observation
  175.         final RealMatrix posMatrix = new Array2DRowRealMatrix(3, 3);
  176.         for (int i = 0; i <= 2; i++) {
  177.             final RealVector position = losMatrix.getColumnVector(i).
  178.                                                   mapMultiply(slantRanges.getEntry(i, 0)).add(rSite.getColumnVector(i));
  179.             posMatrix.setRowVector(i, position);
  180.         }
  181.         // At this point, the proper Gauss Initial Orbit determination is ending because we have the 3 positions of the
  182.         // satellite from the 3 observations. However, we could also calculate the velocity using the next f and g
  183.         // coefficients, see Vallado 7.3.2 and Vallado 2.3.1 for more details
  184.         final double pos2Norm     = posMatrix.getRowVector(1).getNorm();
  185.         final double pos2NormCubed = pos2Norm * pos2Norm * pos2Norm;

  186.         // mathematical matrix and coefficients, see Curtis algorithms 5.5 and 5.6. It is still the IOD GAUSS
  187.         final double f1 = 1. - (0.5 * mu * tau1 * tau1 / pos2NormCubed);
  188.         final double f3 = 1. - (0.5 * mu * tau3 * tau3 / pos2NormCubed);
  189.         final double g1 = tau1 - ((1. / 6.) * mu * tau1 * tau1 * tau1 / pos2NormCubed);
  190.         final double g3 = tau3 - ((1. / 6.) * mu * tau3 * tau3 * tau3 / pos2NormCubed);

  191.         final double v2EquationCoeff = 1. / (f1 * g3 - f3 * g1);
  192.         // velocity at the central position of the satellite, corresponding to the second observation
  193.         final RealVector v2 = (posMatrix.getRowVector(0).mapMultiply(-f3).add(
  194.                 posMatrix.getRowVector(2).mapMultiply(f1))).mapMultiply(v2EquationCoeff);

  195.         // position at the central position of the satellite, corresponding to the second observation
  196.         final RealVector p2 = posMatrix.getRowVector(1);

  197.         // We can finally build the Orekit Object, PVCoordinates and Orbit from p2 and v2
  198.         final Vector3D p2Vector3D = new Vector3D(p2.toArray());
  199.         final Vector3D v2Vector3D = new Vector3D(v2.toArray());
  200.         return new CartesianOrbit(new PVCoordinates(p2Vector3D, v2Vector3D), outputFrame, obsDate2, mu);
  201.     }

  202. }