IodLaplace.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.util.FastMath;
  24. import org.orekit.estimation.measurements.AngularAzEl;
  25. import org.orekit.estimation.measurements.AngularRaDec;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.orbits.CartesianOrbit;
  28. import org.orekit.orbits.Orbit;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.utils.PVCoordinates;

  31. /**
  32.  * Laplace angles-only Initial Orbit Determination (IOD) algorithm, assuming Keplerian motion.
  33.  * <p>
  34.  * Laplace algorithm is one of the first method to determine orbits.
  35.  * An orbit is determined from three lines of sight w.r.t. their respective observers
  36.  * inertial positions vectors. For Laplace method, the observer is identical for all
  37.  * observations.
  38.  *
  39.  * Reference:
  40.  *    Bate, R., Mueller, D. D., &amp; White, J. E. (1971). Fundamentals of astrodynamics.
  41.  *    New York: Dover Publications.
  42.  * </p>
  43.  * @author Shiva Iyer
  44.  * @since 10.1
  45.  */
  46. public class IodLaplace {

  47.     /** Gravitational constant. */
  48.     private final double mu;

  49.     /** Constructor.
  50.      *
  51.      * @param mu  gravitational constant
  52.      */
  53.     public IodLaplace(final double mu) {
  54.         this.mu = mu;
  55.     }

  56.     /** Estimate the orbit from three angular observations at the same location.
  57.      *
  58.      * @param outputFrame Observer coordinates at time of raDec2
  59.      * @param azEl1 first angular observation
  60.      * @param azEl2 second angular observation
  61.      * @param azEl3 third angular observation
  62.      * @return estimate of the orbit at the central date or null if
  63.      *         no estimate is possible with the given data
  64.      * @since 12.0
  65.      */
  66.     public Orbit estimate(final Frame outputFrame,
  67.                           final AngularAzEl azEl1, final AngularAzEl azEl2,
  68.                           final AngularAzEl azEl3) {
  69.         return estimate(outputFrame, azEl2.getGroundStationCoordinates(outputFrame),
  70.                         azEl1.getDate(), azEl1.getObservedLineOfSight(outputFrame),
  71.                         azEl2.getDate(), azEl2.getObservedLineOfSight(outputFrame),
  72.                         azEl3.getDate(), azEl3.getObservedLineOfSight(outputFrame));
  73.     }

  74.     /** Estimate the orbit from three angular observations at the same location.
  75.      *
  76.      * @param outputFrame Observer coordinates at time of raDec2
  77.      * @param raDec1 first angular observation
  78.      * @param raDec2 second angular observation
  79.      * @param raDec3 third angular observation
  80.      * @return estimate of the orbit at the central date or null if
  81.      *         no estimate is possible with the given data
  82.      * @since 11.0
  83.      */
  84.     public Orbit estimate(final Frame outputFrame,
  85.                           final AngularRaDec raDec1, final AngularRaDec raDec2,
  86.                           final AngularRaDec raDec3) {
  87.         return estimate(outputFrame, raDec2.getGroundStationCoordinates(outputFrame),
  88.                         raDec1.getDate(), raDec1.getObservedLineOfSight(outputFrame),
  89.                         raDec2.getDate(), raDec2.getObservedLineOfSight(outputFrame),
  90.                         raDec3.getDate(), raDec3.getObservedLineOfSight(outputFrame));
  91.     }

  92.     /** Estimate orbit from three line of sight angles at the same location.
  93.      *
  94.      * @param outputFrame inertial frame for observer coordinates and orbit estimate
  95.      * @param obsPva Observer coordinates at time obsDate2
  96.      * @param obsDate1 date of observation 1
  97.      * @param los1 line of sight unit vector 1
  98.      * @param obsDate2 date of observation 2
  99.      * @param los2 line of sight unit vector 2
  100.      * @param obsDate3 date of observation 3
  101.      * @param los3 line of sight unit vector 3
  102.      * @return estimate of the orbit at the central date obsDate2 or null if
  103.      *         no estimate is possible with the given data
  104.      */
  105.     public Orbit estimate(final Frame outputFrame, final PVCoordinates obsPva,
  106.                           final AbsoluteDate obsDate1, final Vector3D los1,
  107.                           final AbsoluteDate obsDate2, final Vector3D los2,
  108.                           final AbsoluteDate obsDate3, final Vector3D los3) {

  109.         // The first observation is taken as t1 = 0
  110.         final double t2 = obsDate2.durationFrom(obsDate1);
  111.         final double t3 = obsDate3.durationFrom(obsDate1);

  112.         // Calculate the first and second derivatives of the Line Of Sight vector at t2
  113.         final Vector3D Ldot = los1.scalarMultiply((t2 - t3) / (t2 * t3)).
  114.                                   add(los2.scalarMultiply((2.0 * t2 - t3) / (t2 * (t2 - t3)))).
  115.                                   add(los3.scalarMultiply(t2 / (t3 * (t3 - t2))));
  116.         final Vector3D Ldotdot = los1.scalarMultiply(2.0 / (t2 * t3)).
  117.                                      add(los2.scalarMultiply(2.0 / (t2 * (t2 - t3)))).
  118.                                      add(los3.scalarMultiply(2.0 / (t3 * (t3 - t2))));

  119.         // The determinant will vanish if the observer lies in the plane of the orbit at t2
  120.         final double D = 2.0 * getDeterminant(los2, Ldot, Ldotdot);
  121.         if (FastMath.abs(D) < 1.0E-14) {
  122.             return null;
  123.         }

  124.         final double Dsq   = D * D;
  125.         final double R     = obsPva.getPosition().getNorm();
  126.         final double RdotL = obsPva.getPosition().dotProduct(los2);

  127.         final double D1 = getDeterminant(los2, Ldot, obsPva.getAcceleration());
  128.         final double D2 = getDeterminant(los2, Ldot, obsPva.getPosition());

  129.         // Coefficients of the 8th order polynomial we need to solve to determine "r"
  130.         final double[] coeff = new double[] {-4.0 * mu * mu * D2 * D2 / Dsq,
  131.                                              0.0,
  132.                                              0.0,
  133.                                              4.0 * mu * D2 * (RdotL / D - 2.0 * D1 / Dsq),
  134.                                              0.0,
  135.                                              0.0,
  136.                                              4.0 * D1 * RdotL / D - 4.0 * D1 * D1 / Dsq - R * R, 0.0,
  137.                                              1.0};

  138.         // Use the Laguerre polynomial solver and take the initial guess to be
  139.         // 5 times the observer's position magnitude
  140.         final LaguerreSolver solver = new LaguerreSolver(1E-10, 1E-10, 1E-10);
  141.         final Complex[]      roots  = solver.solveAllComplex(coeff, 5.0 * R);

  142.         // We consider "r" to be the positive real root with the largest magnitude
  143.         double rMag = 0.0;
  144.         for (Complex root : roots) {
  145.             if (root.getReal() > rMag &&
  146.                 FastMath.abs(root.getImaginary()) < solver.getAbsoluteAccuracy()) {
  147.                 rMag = root.getReal();
  148.             }
  149.         }
  150.         if (rMag == 0.0) {
  151.             return null;
  152.         }

  153.         // Calculate rho, the slant range from the observer to the satellite at t2.
  154.         // This yields the "r" vector, which is the satellite's position vector at t2.
  155.         final double   rCubed = rMag * rMag * rMag;
  156.         final double   rho    = -2.0 * D1 / D - 2.0 * mu * D2 / (D * rCubed);
  157.         final Vector3D posVec = los2.scalarMultiply(rho).add(obsPva.getPosition());

  158.         // Calculate rho_dot at t2, which will yield the satellite's velocity vector at t2
  159.         final double D3     = getDeterminant(los2, obsPva.getAcceleration(), Ldotdot);
  160.         final double D4     = getDeterminant(los2, obsPva.getPosition(), Ldotdot);
  161.         final double rhoDot = -D3 / D - mu * D4 / (D * rCubed);
  162.         final Vector3D velVec = los2.scalarMultiply(rhoDot).
  163.                                     add(Ldot.scalarMultiply(rho)).
  164.                                     add(obsPva.getVelocity());

  165.         // Return the estimated orbit
  166.         return new CartesianOrbit(new PVCoordinates(posVec, velVec), outputFrame, obsDate2, mu);
  167.     }

  168.     /** Calculate the determinant of the matrix with given column vectors.
  169.      *
  170.      * @param col0 Matrix column 0
  171.      * @param col1 Matrix column 1
  172.      * @param col2 Matrix column 2
  173.      * @return matrix determinant
  174.      *
  175.      */
  176.     private double getDeterminant(final Vector3D col0, final Vector3D col1, final Vector3D col2) {
  177.         final Array2DRowRealMatrix mat = new Array2DRowRealMatrix(3, 3);
  178.         mat.setColumn(0, col0.toArray());
  179.         mat.setColumn(1, col1.toArray());
  180.         mat.setColumn(2, col2.toArray());
  181.         return new LUDecomposition(mat).getDeterminant();
  182.     }

  183. }