OrbitElementsType.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.files.ccsds.ndm.odm.ocm;

  18. import java.util.List;
  19. import java.util.stream.Collectors;
  20. import java.util.stream.Stream;

  21. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathUtils;
  24. import org.hipparchus.util.SinCos;
  25. import org.orekit.annotation.DefaultDataContext;
  26. import org.orekit.bodies.GeodeticPoint;
  27. import org.orekit.bodies.OneAxisEllipsoid;
  28. import org.orekit.errors.OrekitException;
  29. import org.orekit.errors.OrekitMessages;
  30. import org.orekit.frames.Frame;
  31. import org.orekit.frames.FramesFactory;
  32. import org.orekit.orbits.EquinoctialOrbit;
  33. import org.orekit.orbits.KeplerianOrbit;
  34. import org.orekit.orbits.PositionAngleType;
  35. import org.orekit.time.AbsoluteDate;
  36. import org.orekit.utils.TimeStampedPVCoordinates;
  37. import org.orekit.utils.units.Unit;

  38. /** Orbit element set type used in CCSDS {@link Ocm Orbit Comprehensive Messages}.
  39.  * @see <a href="https://sanaregistry.org/r/orbital_elements">SANA registry for orbital elements</a>
  40.  * @author Luc Maisonobe
  41.  * @since 11.0
  42.  */
  43. public enum OrbitElementsType {

  44.     // CHECKSTYLE: stop MultipleStringLiterals check

  45.     /** Spherical 6-element set (α,δ,β,A,r,v). */
  46.     ADBARV("Spherical 6-element set (α,δ,β,A,r,v)",
  47.            "°", "°", "°", "°", "km", "km/s"),

  48.     /** Cartesian 3-element position (X, Y, Z). */
  49.     CARTP("Cartesian 3-element position (X, Y, Z)",
  50.           "km", "km", "km") {

  51.         /** {@inheritDoc} */
  52.         @Override
  53.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  54.                                                     final OneAxisEllipsoid body, final double mu) {
  55.             return new TimeStampedPVCoordinates(date,
  56.                                                 new Vector3D(elements[0], elements[1], elements[2]),
  57.                                                 Vector3D.ZERO,
  58.                                                 Vector3D.ZERO);
  59.         }

  60.         /** {@inheritDoc} */
  61.         @Override
  62.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  63.                                       final OneAxisEllipsoid body, final double mu) {
  64.             return new double[] {
  65.                 pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ()
  66.             };
  67.         }

  68.     },

  69.     /** Cartesian 6-element position and velocity (X, Y, Z, XD, YD, ZD). */
  70.     CARTPV("Cartesian 6-element position and velocity (X, Y, Z, XD, YD, ZD)",
  71.            "km", "km", "km", "km/s", "km/s", "km/s") {

  72.         /** {@inheritDoc} */
  73.         @Override
  74.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  75.                                                     final OneAxisEllipsoid body, final double mu) {
  76.             return new TimeStampedPVCoordinates(date,
  77.                                                 new Vector3D(elements[0], elements[1], elements[2]),
  78.                                                 new Vector3D(elements[3], elements[4], elements[5]),
  79.                                                 Vector3D.ZERO);
  80.         }

  81.         /** {@inheritDoc} */
  82.         @Override
  83.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  84.                                       final OneAxisEllipsoid body, final double mu) {
  85.             return new double[] {
  86.                 pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(),
  87.                 pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ()
  88.             };
  89.         }

  90.     },

  91.     /** Cartesian 9-element position, velocity and acceleration (X, Y, Z, XD, YD, ZD, XDD, YDD, ZDD). */
  92.     CARTPVA("Cartesian 9-element position, velocity and acceleration (X, Y, Z, XD, YD, ZD, XDD, YDD, ZDD)",
  93.             "km", "km", "km", "km/s", "km/s", "km/s", "km/s²", "km/s²", "km/s²") {

  94.         /** {@inheritDoc} */
  95.         @Override
  96.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  97.                                                     final OneAxisEllipsoid body, final double mu) {
  98.             return new TimeStampedPVCoordinates(date,
  99.                                                 new Vector3D(elements[0], elements[1], elements[2]),
  100.                                                 new Vector3D(elements[3], elements[4], elements[5]),
  101.                                                 new Vector3D(elements[6], elements[7], elements[8]));
  102.         }

  103.         /** {@inheritDoc} */
  104.         @Override
  105.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  106.                                       final OneAxisEllipsoid body, final double mu) {
  107.             return new double[] {
  108.                 pv.getPosition().getX(),     pv.getPosition().getY(),     pv.getPosition().getZ(),
  109.                 pv.getVelocity().getX(),     pv.getVelocity().getY(),     pv.getVelocity().getZ(),
  110.                 pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ()
  111.             };
  112.         }

  113.     },

  114.     /** Delaunay elements (L, G, H, l, g, h). */
  115.     DELAUNAY("Delaunay elements (L, G, H, l, g, h)",
  116.              "km²/s", "km²/s", "km²/s", "°", "°", "°"),

  117.     /** Modified Delaunay elements (Lm, Gm, Hm, lm, gm, hm). */
  118.     DELAUNAYMOD("Delaunay elements (Lm, Gm, Hm, lm, gm, hm)",
  119.                 "√km", "√km", "√km", "°", "°", "°"),

  120.     /** 12 elements eigenvalue/eigenvectors (EigMaj, EigMed, EigMin, EigVecMaj, EigVecMed, EigVecMin). */
  121.     EIGVAL3EIGVEC3("12 elements eigenvalue/eigenvectors (EigMaj, EigMed, EigMin, EigVecMaj, EigVecMed, EigVecMin)",
  122.                    "km", "km", "km", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a"),

  123.     /** Equinoctial elements (a, af, ag, L=M+ω+frΩ, χ, ψ, fr). */
  124.     EQUINOCTIAL("Equinoctial elements (a, af, ag, L=M+ω+frΩ, χ, ψ, fr)",
  125.                 "km", "n/a", "n/a", "°", "n/a", "n/a", "n/a") {

  126.         /** {@inheritDoc} */
  127.         @Override
  128.         @DefaultDataContext
  129.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  130.                                                     final OneAxisEllipsoid body, final double mu) {
  131.             if (elements[6] < 0) {
  132.                 // retrograde
  133.                 throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL,
  134.                                           EQUINOCTIAL.name());
  135.             }
  136.             return new EquinoctialOrbit(elements[0], elements[1], elements[2],
  137.                                         elements[5], elements[4], // BEWARE! the inversion here is intentional
  138.                                         elements[3], PositionAngleType.MEAN,
  139.                                         FramesFactory.getGCRF(), date, mu).
  140.                             getPVCoordinates();
  141.         }

  142.         /** {@inheritDoc} */
  143.         @Override
  144.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  145.                                       final OneAxisEllipsoid body, final double mu) {
  146.             final EquinoctialOrbit orbit = new EquinoctialOrbit(pv, frame, mu);
  147.             return new double[] {
  148.                 orbit.getA(), orbit.getEquinoctialEx(), orbit.getEquinoctialEy(),
  149.                 orbit.getLM(), orbit.getHy(), orbit.getHx(), +1
  150.             };
  151.         }

  152.     },

  153.     /** Modified equinoctial elements (p=a(1−e²), af, ag, L'=υ+ω+frΩ, χ, ψ, fr). */
  154.     EQUINOCTIALMOD("Modified equinoctial elements (p=a(1−e²), af, ag, L'=υ+ω+frΩ, χ, ψ, fr)",
  155.                    "km", "n/a", "n/a", "°", "n/a", "n/a", "n/a") {

  156.         /** {@inheritDoc} */
  157.         @Override
  158.         @DefaultDataContext
  159.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  160.                                                     final OneAxisEllipsoid body, final double mu) {
  161.             if (elements[6] < 0) {
  162.                 // retrograde
  163.                 throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL,
  164.                                           EQUINOCTIALMOD.name());
  165.             }
  166.             final double oMe2 = 1.0 - (elements[1] * elements[1] + elements[2] * elements[2]);
  167.             return new EquinoctialOrbit(elements[0] / oMe2, elements[1], elements[2],
  168.                                         elements[5], elements[4], // BEWARE! the inversion here is intentional
  169.                                         elements[3], PositionAngleType.TRUE,
  170.                                         FramesFactory.getGCRF(), date, mu).
  171.                             getPVCoordinates();
  172.         }

  173.         /** {@inheritDoc} */
  174.         @Override
  175.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  176.                                       final OneAxisEllipsoid body, final double mu) {
  177.             final EquinoctialOrbit orbit = new EquinoctialOrbit(pv, frame, mu);
  178.             final double           ex    = orbit.getEquinoctialEx();
  179.             final double           ey    = orbit.getEquinoctialEy();
  180.             return new double[] {
  181.                 orbit.getA() * (1 - (ex * ex + ey * ey)), ex, ey,
  182.                 orbit.getLv(), orbit.getHy(), orbit.getHx(), +1
  183.             };
  184.         }

  185.     },

  186.     /** Geodetic elements (λ, ΦGD, β, A, h, vre). */
  187.     GEODETIC("Geodetic elements (λ, ΦGD, β, A, h, vre)",
  188.              "°", "°", "°", "°", "km", "km/s") {

  189.         /** {@inheritDoc} */
  190.         @Override
  191.         @DefaultDataContext
  192.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  193.                                                     final OneAxisEllipsoid body, final double mu) {
  194.             final GeodeticPoint gp       = new GeodeticPoint(elements[1], elements[0], elements[4]);
  195.             final Vector3D      position = body.transform(gp);
  196.             final SinCos        scBeta   = FastMath.sinCos(elements[2]);
  197.             final SinCos        scAzi    = FastMath.sinCos(elements[3]);
  198.             final Vector3D      velocity = new Vector3D(elements[5] * scBeta.cos() * scAzi.sin(), gp.getEast(),
  199.                                                         elements[5] * scBeta.cos() * scAzi.cos(), gp.getNorth(),
  200.                                                         elements[5] * scBeta.sin(), gp.getZenith());
  201.             return new TimeStampedPVCoordinates(date, position, velocity);
  202.         }

  203.         /** {@inheritDoc} */
  204.         @Override
  205.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  206.                                       final OneAxisEllipsoid body, final double mu) {
  207.             final GeodeticPoint gp = body.transform(pv.getPosition(), frame, pv.getDate());
  208.             return new double[] {
  209.                 gp.getLongitude(), gp.getLatitude(),
  210.                 MathUtils.SEMI_PI - Vector3D.angle(pv.getVelocity(), gp.getZenith()),
  211.                 FastMath.atan2(Vector3D.dotProduct(pv.getVelocity(), gp.getEast()),
  212.                                Vector3D.dotProduct(pv.getVelocity(), gp.getNorth())),
  213.                 gp.getAltitude(),
  214.                 pv.getVelocity().getNorm()
  215.             };
  216.         }

  217.     },

  218.     /** Keplerian 6-element classical set (a, e, i, Ω, ω, ν). */
  219.     KEPLERIAN("Keplerian 6-elemnt classical set (a, e, i, Ω, ω, ν)",
  220.               "km", "n/a", "°", "°", "°", "°") {

  221.         /** {@inheritDoc} */
  222.         @Override
  223.         @DefaultDataContext
  224.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  225.                                                     final OneAxisEllipsoid body, final double mu) {
  226.             return new KeplerianOrbit(elements[0], elements[1], elements[2],
  227.                                       elements[4], elements[3], // BEWARE! the inversion here is intentional
  228.                                       elements[5], PositionAngleType.TRUE,
  229.                                       Frame.getRoot(), date, mu).
  230.                    getPVCoordinates();
  231.         }

  232.         /** {@inheritDoc} */
  233.         @Override
  234.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  235.                                       final OneAxisEllipsoid body, final double mu) {
  236.             final KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, mu);
  237.             return new double[] {
  238.                 orbit.getA(), orbit.getE(), orbit.getI(),
  239.                 orbit.getRightAscensionOfAscendingNode(),
  240.                 orbit.getPerigeeArgument(), orbit.getTrueAnomaly()
  241.             };
  242.         }

  243.     },

  244.     /** Keplerian 6-element classical set (a, e, i, Ω, ω, M). */
  245.     KEPLERIANMEAN("Keplerian 6-elemnt classical set (a, e, i, Ω, ω, M)",
  246.                   "km", "n/a", "°", "°", "°", "°") {

  247.         /** {@inheritDoc} */
  248.         @Override
  249.         @DefaultDataContext
  250.         public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  251.                                                     final OneAxisEllipsoid body, final double mu) {
  252.             return new KeplerianOrbit(elements[0], elements[1], elements[2],
  253.                                       elements[4], elements[3], // BEWARE! the inversion here is intentional
  254.                                       elements[5], PositionAngleType.MEAN,
  255.                                       FramesFactory.getGCRF(), date, mu).
  256.                    getPVCoordinates();
  257.         }

  258.         /** {@inheritDoc} */
  259.         @Override
  260.         public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  261.                                       final OneAxisEllipsoid body, final double mu) {
  262.             final KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, mu);
  263.             return new double[] {
  264.                 orbit.getA(), orbit.getE(), orbit.getI(),
  265.                 orbit.getRightAscensionOfAscendingNode(),
  266.                 orbit.getPerigeeArgument(), orbit.getMeanAnomaly()
  267.             };
  268.         }

  269.     },

  270.     /** Modified spherical 6-element set (λ, δ, β, A, r, v). */
  271.     LDBARV("Modified spherical 6-element set (λ, δ, β, A, r, v)",
  272.            "°", "°", "°", "°", "km", "km/s"),

  273.     /** Geosynchronous on-station tailored set (a, ex, ey, ix, iy, λ). */
  274.     ONSTATION("Geosynchronous on-station tailored set (a, ex, ey, ix, iy, λ)",
  275.               "km", "n/a", "n/a", "n/a", "n/a", "°"),

  276.     /** Canonical counterpart of equinoctial 6-element set (λM=M+ω+Ω, gp, hp, Lp, Gp, Hp). */
  277.     POINCARE("Canonical counterpart of equinoctial 6-element set (λM=M+ω+Ω, gp, hp, Lp, Gp, Hp)",
  278.              "°", "km/√s", "km/√s", "km²/s", "km/√s", "km/√s");

  279.     // CHECKSTYLE: resume MultipleStringLiterals check

  280.     /** Description. */
  281.     private final String description;

  282.     /** Elements units. */
  283.     private final List<Unit> units;

  284.     /** Simple constructor.
  285.      * @param description description
  286.      * @param unitsSpecifications elements units specifications
  287.      */
  288.     OrbitElementsType(final String description, final String... unitsSpecifications) {
  289.         this.description = description;
  290.         this.units       = Stream.of(unitsSpecifications).
  291.                            map(Unit::parse).
  292.                            collect(Collectors.toList());
  293.     }

  294.     /** Get the elements units.
  295.      * @return elements units
  296.      */
  297.     public List<Unit> getUnits() {
  298.         return units;
  299.     }

  300.     /** Convert to Cartesian coordinates.
  301.      * @param date elements date
  302.      * @param elements elements values in SI units
  303.      * @param body central body
  304.      * (may be null if type is <em>not</em> {@link OrbitElementsType#GEODETIC})
  305.      * @param mu gravitational parameter in m³/s²
  306.      * @return Cartesian coordinates
  307.      */
  308.     public TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final double[] elements,
  309.                                                 final OneAxisEllipsoid body, final double mu) {
  310.         throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE, name(), toString());
  311.     }

  312.     /** Convert to raw elements array.
  313.      * @param pv Cartesian coordinates
  314.      * @param frame inertial frame where elements are defined
  315.      * @param body central body
  316.      * (may be null if type is <em>not</em> {@link OrbitElementsType#GEODETIC})
  317.      * @param mu gravitational parameter in m³/s²
  318.      * @return elements elements values in SI units
  319.      * @since 12.0
  320.      */
  321.     public double[] toRawElements(final TimeStampedPVCoordinates pv, final Frame frame,
  322.                                   final OneAxisEllipsoid body, final double mu) {
  323.         throw new OrekitException(OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE, name(), toString());
  324.     }

  325.     /** {@inheritDoc} */
  326.     @Override
  327.     public String toString() {
  328.         return description;
  329.     }

  330. }