IodLambert.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.geometry.euclidean.twod.Vector2D;
  20. import org.orekit.control.heuristics.lambert.LambertBoundaryConditions;
  21. import org.orekit.control.heuristics.lambert.LambertBoundaryVelocities;
  22. import org.orekit.control.heuristics.lambert.LambertSolver;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.estimation.measurements.PV;
  26. import org.orekit.estimation.measurements.Position;
  27. import org.orekit.frames.Frame;
  28. import org.orekit.orbits.CartesianOrbit;
  29. import org.orekit.orbits.Orbit;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.utils.TimeStampedPVCoordinates;

  32. /**
  33.  * Lambert position-based Initial Orbit Determination (IOD) algorithm, assuming Keplerian motion.
  34.  * <p>
  35.  * An orbit is determined from two position vectors.
  36.  * @author Joris Olympio
  37.  * @see LambertSolver
  38.  * @since 8.0
  39.  */
  40. public class IodLambert {

  41.     /** gravitational constant. */
  42.     private final double mu;

  43.     /** Creator.
  44.      *
  45.      * @param mu gravitational constant
  46.      */
  47.     public IodLambert(final double mu) {
  48.         this.mu = mu;
  49.     }

  50.     /** Estimate an initial orbit from two position measurements.
  51.      * <p>
  52.      * The logic for setting {@code posigrade} and {@code nRev} is that the
  53.      * sweep angle Δυ travelled by the object between {@code t1} and {@code t2} is
  54.      * 2π {@code nRev +1} - α if {@code posigrade} is false and 2π {@code nRev} + α
  55.      * if {@code posigrade} is true, where α is the separation angle between
  56.      * {@code p1} and {@code p2}, which is always computed between 0 and π
  57.      * (because in 3D without a normal reference, vector angles cannot go past π).
  58.      * </p>
  59.      * <p>
  60.      * This implies that {@code posigrade} should be set to true if {@code p2} is
  61.      * located in the half orbit starting at {@code p1} and it should be set to
  62.      * false if {@code p2} is located in the half orbit ending at {@code p1},
  63.      * regardless of the number of periods between {@code t1} and {@code t2},
  64.      * and {@code nRev} should be set accordingly.
  65.      * </p>
  66.      * <p>
  67.      * As an example, if {@code t2} is less than half a period after {@code t1},
  68.      * then {@code posigrade} should be {@code true} and {@code nRev} should be 0.
  69.      * If {@code t2} is more than half a period after {@code t1} but less than
  70.      * one period after {@code t1}, {@code posigrade} should be {@code false} and
  71.      * {@code nRev} should be 0.
  72.      * </p>
  73.      * @param frame     measurements frame
  74.      * @param posigrade flag indicating the direction of motion
  75.      * @param nRev      number of revolutions
  76.      * @param p1        first position measurement
  77.      * @param p2        second position measurement
  78.      * @return an initial Keplerian orbit estimation at the first observation date t1
  79.      * @since 11.0
  80.      */
  81.     public Orbit estimate(final Frame frame, final boolean posigrade,
  82.                           final int nRev, final Position p1,  final Position p2) {
  83.         return estimate(frame, posigrade, nRev,
  84.                         p1.getPosition(), p1.getDate(), p2.getPosition(), p2.getDate());
  85.     }

  86.     /** Estimate an initial orbit from two PV measurements.
  87.      * <p>
  88.      * The logic for setting {@code posigrade} and {@code nRev} is that the
  89.      * sweep angle Δυ travelled by the object between {@code t1} and {@code t2} is
  90.      * 2π {@code nRev +1} - α if {@code posigrade} is false and 2π {@code nRev} + α
  91.      * if {@code posigrade} is true, where α is the separation angle between
  92.      * {@code p1} and {@code p2}, which is always computed between 0 and π
  93.      * (because in 3D without a normal reference, vector angles cannot go past π).
  94.      * </p>
  95.      * <p>
  96.      * This implies that {@code posigrade} should be set to true if {@code p2} is
  97.      * located in the half orbit starting at {@code p1} and it should be set to
  98.      * false if {@code p2} is located in the half orbit ending at {@code p1},
  99.      * regardless of the number of periods between {@code t1} and {@code t2},
  100.      * and {@code nRev} should be set accordingly.
  101.      * </p>
  102.      * <p>
  103.      * As an example, if {@code t2} is less than half a period after {@code t1},
  104.      * then {@code posigrade} should be {@code true} and {@code nRev} should be 0.
  105.      * If {@code t2} is more than half a period after {@code t1} but less than
  106.      * one period after {@code t1}, {@code posigrade} should be {@code false} and
  107.      * {@code nRev} should be 0.
  108.      * </p>
  109.      * @param frame     measurements frame
  110.      * @param posigrade flag indicating the direction of motion
  111.      * @param nRev      number of revolutions
  112.      * @param pv1       first PV measurement
  113.      * @param pv2       second PV measurement
  114.      * @return an initial Keplerian orbit estimation at the first observation date t1
  115.      * @since 12.0
  116.      */
  117.     public Orbit estimate(final Frame frame, final boolean posigrade,
  118.                           final int nRev, final PV pv1,  final PV pv2) {
  119.         return estimate(frame, posigrade, nRev,
  120.                         pv1.getPosition(), pv1.getDate(), pv2.getPosition(), pv2.getDate());
  121.     }

  122.     /** Estimate a Keplerian orbit given two position vectors and a duration.
  123.      * <p>
  124.      * The logic for setting {@code posigrade} and {@code nRev} is that the
  125.      * sweep angle Δυ travelled by the object between {@code t1} and {@code t2} is
  126.      * 2π {@code nRev +1} - α if {@code posigrade} is false and 2π {@code nRev} + α
  127.      * if {@code posigrade} is true, where α is the separation angle between
  128.      * {@code p1} and {@code p2}, which is always computed between 0 and π
  129.      * (because in 3D without a normal reference, vector angles cannot go past π).
  130.      * </p>
  131.      * <p>
  132.      * This implies that {@code posigrade} should be set to true if {@code p2} is
  133.      * located in the half orbit starting at {@code p1} and it should be set to
  134.      * false if {@code p2} is located in the half orbit ending at {@code p1},
  135.      * regardless of the number of periods between {@code t1} and {@code t2},
  136.      * and {@code nRev} should be set accordingly.
  137.      * </p>
  138.      * <p>
  139.      * As an example, if {@code t2} is less than half a period after {@code t1},
  140.      * then {@code posigrade} should be {@code true} and {@code nRev} should be 0.
  141.      * If {@code t2} is more than half a period after {@code t1} but less than
  142.      * one period after {@code t1}, {@code posigrade} should be {@code false} and
  143.      * {@code nRev} should be 0.
  144.      * </p>
  145.      * @param frame     frame
  146.      * @param posigrade flag indicating the direction of motion
  147.      * @param nRev      number of revolutions
  148.      * @param p1        position vector 1
  149.      * @param t1        date of observation 1
  150.      * @param p2        position vector 2
  151.      * @param t2        date of observation 2
  152.      * @return  an initial Keplerian orbit estimate at the first observation date t1
  153.      */
  154.     public Orbit estimate(final Frame frame, final boolean posigrade,
  155.                           final int nRev,
  156.                           final Vector3D p1, final AbsoluteDate t1,
  157.                           final Vector3D p2, final AbsoluteDate t2) {
  158.         // Exception if t2 < t1
  159.         final double tau = t2.durationFrom(t1); // in seconds
  160.         if (tau < 0.) {
  161.             throw new OrekitException(OrekitMessages.NON_CHRONOLOGICAL_DATES_FOR_OBSERVATIONS, t1, t2, -tau);
  162.         }
  163.         final LambertSolver solver = new LambertSolver(mu);
  164.         final LambertBoundaryConditions boundaryConditions = new LambertBoundaryConditions(t1, p1, t2, p2,
  165.                 frame);
  166.         final LambertBoundaryVelocities velocities = solver.solve(posigrade, nRev, boundaryConditions);
  167.         if (velocities == null) {
  168.             return null;
  169.         } else {
  170.             return new CartesianOrbit(new TimeStampedPVCoordinates(t1, p1, velocities.getInitialVelocity()),
  171.                     frame, mu);
  172.         }
  173.     }

  174.     /** Lambert's solver for the historical, planar problem.
  175.      * Assume mu=1.
  176.      *
  177.      * @param r1 radius 1
  178.      * @param r2  radius 2
  179.      * @param dth sweep angle
  180.      * @param tau time of flight
  181.      * @param mRev number of revs
  182.      * @param V1 velocity at departure in (T, N) basis
  183.      * @return exit flag
  184.      * @deprecated as of 13.1, use {@link LambertSolver}
  185.      */
  186.     boolean solveLambertPb(final double r1, final double r2, final double dth, final double tau,
  187.                            final int mRev, final double[] V1) {
  188.         final Vector2D solution = LambertSolver.solveNormalized2D(r1, r2, dth, tau, mRev);
  189.         if (solution == Vector2D.NaN) {
  190.             return false;
  191.         } else {
  192.             V1[0] = solution.getX();
  193.             V1[1] = solution.getY();
  194.             return true;
  195.         }
  196.     }

  197. }