JPLCelestialBody.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.bodies;


  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.geometry.euclidean.threed.Rotation;
  23. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.hipparchus.util.Precision;
  26. import org.orekit.frames.FieldStaticTransform;
  27. import org.orekit.frames.FieldTransform;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.frames.StaticTransform;
  30. import org.orekit.frames.Transform;
  31. import org.orekit.frames.TransformProvider;
  32. import org.orekit.time.AbsoluteDate;
  33. import org.orekit.time.FieldAbsoluteDate;
  34. import org.orekit.utils.FieldPVCoordinates;
  35. import org.orekit.utils.PVCoordinates;
  36. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  37. import org.orekit.utils.TimeStampedPVCoordinates;

  38. /** Implementation of the {@link CelestialBody} interface using JPL or INPOP ephemerides.
  39.  * @author Luc Maisonobe
  40.  */
  41. class JPLCelestialBody implements CelestialBody {

  42.     /** Name of the body. */
  43.     private final String name;

  44.     /** Regular expression for supported files names. */
  45.     private final String supportedNames;

  46.     /** Ephemeris type to generate. */
  47.     private final JPLEphemeridesLoader.EphemerisType generateType;

  48.     /** Raw position-velocity provider. */
  49.     private final transient JPLEphemeridesLoader.RawPVProvider rawPVProvider;

  50.     /** Attraction coefficient of the body (m³/s²). */
  51.     private final double gm;

  52.     /** Scaling factor for position-velocity. */
  53.     private final double scale;

  54.     /** IAU pole. */
  55.     private final IAUPole iauPole;

  56.     /** Inertially oriented, body-centered frame. */
  57.     private final Frame inertialFrame;

  58.     /** Body oriented, body-centered frame. */
  59.     private final Frame bodyFrame;

  60.     /** Build an instance and the underlying frame.
  61.      * @param name name of the body
  62.      * @param supportedNames regular expression for supported files names
  63.      * @param generateType ephemeris type to generate
  64.      * @param rawPVProvider raw position-velocity provider
  65.      * @param gm attraction coefficient (in m³/s²)
  66.      * @param scale scaling factor for position-velocity
  67.      * @param iauPole IAU pole implementation
  68.      * @param definingFrameAlignedWithICRF frame in which celestial body coordinates are defined,
  69.      * this frame <strong>must</strong> be aligned with ICRF
  70.      * @param inertialFrameName name to use for inertial frame (if null a default name will be built)
  71.      * @param bodyOrientedFrameName name to use for body-oriented frame (if null a default name will be built)
  72.      */
  73.     JPLCelestialBody(final String name, final String supportedNames,
  74.                      final JPLEphemeridesLoader.EphemerisType generateType,
  75.                      final JPLEphemeridesLoader.RawPVProvider rawPVProvider,
  76.                      final double gm, final double scale,
  77.                      final IAUPole iauPole, final Frame definingFrameAlignedWithICRF,
  78.                      final String inertialFrameName, final String bodyOrientedFrameName) {
  79.         this.name           = name;
  80.         this.gm             = gm;
  81.         this.scale          = scale;
  82.         this.supportedNames = supportedNames;
  83.         this.generateType   = generateType;
  84.         this.rawPVProvider  = rawPVProvider;
  85.         this.iauPole        = iauPole;
  86.         this.inertialFrame  = new InertiallyOriented(definingFrameAlignedWithICRF, inertialFrameName);
  87.         this.bodyFrame      = new BodyOriented(bodyOrientedFrameName);
  88.     }

  89.     /** {@inheritDoc} */
  90.     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {

  91.         // apply the scale factor to raw position-velocity
  92.         final PVCoordinates rawPV    = rawPVProvider.getRawPV(date);
  93.         final TimeStampedPVCoordinates scaledPV = new TimeStampedPVCoordinates(date, scale, rawPV);

  94.         // the raw PV are relative to the parent of the body centered inertially oriented frame
  95.         final Transform transform = getInertiallyOrientedFrame().getParent().getTransformTo(frame, date);

  96.         // convert to requested frame
  97.         return transform.transformPVCoordinates(scaledPV);

  98.     }

  99.     /** Get the {@link FieldPVCoordinates} of the body in the selected frame.
  100.      * @param date current date
  101.      * @param frame the frame where to define the position
  102.      * @param <T> type of the field elements
  103.      * @return time-stamped position/velocity of the body (m and m/s)
  104.      */
  105.     public <T extends CalculusFieldElement<T>> TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date,
  106.                                                                                                  final Frame frame) {

  107.         // apply the scale factor to raw position-velocity
  108.         final FieldPVCoordinates<T>            rawPV    = rawPVProvider.getRawPV(date);
  109.         final TimeStampedFieldPVCoordinates<T> scaledPV = new TimeStampedFieldPVCoordinates<>(date, scale, rawPV);

  110.         // the raw PV are relative to the parent of the body centered inertially oriented frame
  111.         final FieldTransform<T> transform = getInertiallyOrientedFrame().getParent().getTransformTo(frame, date);

  112.         // convert to requested frame
  113.         return transform.transformPVCoordinates(scaledPV);

  114.     }

  115.     /** {@inheritDoc} */
  116.     @Override
  117.     public Vector3D getPosition(final AbsoluteDate date, final Frame frame) {

  118.         // apply the scale factor to raw position
  119.         final Vector3D rawPosition    = rawPVProvider.getRawPosition(date);
  120.         final Vector3D scaledPosition = rawPosition.scalarMultiply(scale);

  121.         // the raw position is relative to the parent of the body centered inertially oriented frame
  122.         final StaticTransform transform = getInertiallyOrientedFrame().getParent().getStaticTransformTo(frame, date);

  123.         // convert to requested frame
  124.         return transform.transformPosition(scaledPosition);
  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public <T extends CalculusFieldElement<T>> FieldVector3D<T> getPosition(final FieldAbsoluteDate<T> date, final Frame frame) {

  129.         // apply the scale factor to raw position
  130.         final FieldVector3D<T> rawPosition     = rawPVProvider.getRawPosition(date);
  131.         final FieldVector3D<T> scaledPosition  = rawPosition.scalarMultiply(scale);

  132.         // the raw position is relative to the parent of the body centered inertially oriented frame
  133.         final FieldStaticTransform<T> transform = getInertiallyOrientedFrame().getParent().getStaticTransformTo(frame, date);

  134.         // convert to requested frame
  135.         return transform.transformPosition(scaledPosition);
  136.     }

  137.     /** {@inheritDoc} */
  138.     public String getName() {
  139.         return name;
  140.     }

  141.     /** {@inheritDoc} */
  142.     public double getGM() {
  143.         return gm;
  144.     }

  145.     /** {@inheritDoc} */
  146.     public Frame getInertiallyOrientedFrame() {
  147.         return inertialFrame;
  148.     }

  149.     /** {@inheritDoc} */
  150.     public Frame getBodyOrientedFrame() {
  151.         return bodyFrame;
  152.     }

  153.     /** Inertially oriented body centered frame. */
  154.     private class InertiallyOriented extends Frame {

  155.         /** Suffix for inertial frame name. */
  156.         private static final String INERTIAL_FRAME_SUFFIX = "/inertial";

  157.         /** Simple constructor.
  158.          * @param definingFrame frame in which celestial body coordinates are defined
  159.          * @param frameName name to use (if null a default name will be built)
  160.          */
  161.         InertiallyOriented(final Frame definingFrame, final String frameName) {
  162.             super(definingFrame, new TransformProvider() {

  163.                 /** {@inheritDoc} */
  164.                 public Transform getTransform(final AbsoluteDate date) {

  165.                     // compute translation from parent frame to self
  166.                     final PVCoordinates pv = getPVCoordinates(date, definingFrame);
  167.                     final Transform translation = new Transform(date, pv.negate());

  168.                     // compute rotation from ICRF frame to self,
  169.                     // as per the "Report of the IAU/IAG Working Group on Cartographic
  170.                     // Coordinates and Rotational Elements of the Planets and Satellites"
  171.                     // These definitions are common for all recent versions of this report
  172.                     // published every three years, the precise values of pole direction
  173.                     // and W angle coefficients may vary from publication year as models are
  174.                     // adjusted. These coefficients are not in this class, they are in the
  175.                     // specialized classes that do implement the getPole and getPrimeMeridianAngle
  176.                     // methods
  177.                     final Vector3D pole  = iauPole.getPole(date);
  178.                     final Vector3D qNode = iauPole.getNode(date);
  179.                     final Transform rotation =
  180.                                     new Transform(date, new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I));

  181.                     // update transform from parent to self
  182.                     return new Transform(date, translation, rotation);

  183.                 }

  184.                 @Override
  185.                 public StaticTransform getStaticTransform(final AbsoluteDate date) {
  186.                     // compute translation from parent frame to self
  187.                     final Vector3D position = getPosition(date, definingFrame);

  188.                     // compute rotation from ICRF frame to self,
  189.                     // as per the "Report of the IAU/IAG Working Group on Cartographic
  190.                     // Coordinates and Rotational Elements of the Planets and Satellites"
  191.                     // These definitions are common for all recent versions of this report
  192.                     // published every three years, the precise values of pole direction
  193.                     // and W angle coefficients may vary from publication year as models are
  194.                     // adjusted. These coefficients are not in this class, they are in the
  195.                     // specialized classes that do implement the getPole and getPrimeMeridianAngle
  196.                     // methods
  197.                     final Vector3D pole  = iauPole.getPole(date);
  198.                     final Vector3D qNode = iauPole.getNode(date);
  199.                     final Rotation rotation =
  200.                                     new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I);

  201.                     // update transform from parent to self
  202.                     return StaticTransform.of(date, position.negate(), rotation);
  203.                 }

  204.                 /** {@inheritDoc} */
  205.                 public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {

  206.                     // compute translation from parent frame to self
  207.                     final FieldPVCoordinates<T> pv = getPVCoordinates(date, definingFrame);
  208.                     final FieldTransform<T> translation = new FieldTransform<>(date, pv.negate());

  209.                     // compute rotation from ICRF frame to self,
  210.                     // as per the "Report of the IAU/IAG Working Group on Cartographic
  211.                     // Coordinates and Rotational Elements of the Planets and Satellites"
  212.                     // These definitions are common for all recent versions of this report
  213.                     // published every three years, the precise values of pole direction
  214.                     // and W angle coefficients may vary from publication year as models are
  215.                     // adjusted. These coefficients are not in this class, they are in the
  216.                     // specialized classes that do implement the getPole and getPrimeMeridianAngle
  217.                     // methods
  218.                     final FieldVector3D<T> pole  = iauPole.getPole(date);
  219.                     FieldVector3D<T> qNode = FieldVector3D.crossProduct(Vector3D.PLUS_K, pole);
  220.                     if (qNode.getNormSq().getReal() < Precision.SAFE_MIN) {
  221.                         qNode = FieldVector3D.getPlusI(date.getField());
  222.                     }
  223.                     final FieldTransform<T> rotation =
  224.                                     new FieldTransform<>(date,
  225.                                                     new FieldRotation<>(pole,
  226.                                                                     qNode,
  227.                                                                     FieldVector3D.getPlusK(date.getField()),
  228.                                                                     FieldVector3D.getPlusI(date.getField())));

  229.                     // update transform from parent to self
  230.                     return new FieldTransform<>(date, translation, rotation);

  231.                 }

  232.                 @Override
  233.                 public <T extends CalculusFieldElement<T>> FieldStaticTransform<T> getStaticTransform(final FieldAbsoluteDate<T> date) {
  234.                     // field
  235.                     final Field<T> field = date.getField();
  236.                     // compute translation from parent frame to self
  237.                     final FieldVector3D<T> position = getPosition(date, definingFrame);

  238.                     // compute rotation from ICRF frame to self,
  239.                     // as per the "Report of the IAU/IAG Working Group on Cartographic
  240.                     // Coordinates and Rotational Elements of the Planets and Satellites"
  241.                     // These definitions are common for all recent versions of this report
  242.                     // published every three years, the precise values of pole direction
  243.                     // and W angle coefficients may vary from publication year as models are
  244.                     // adjusted. These coefficients are not in this class, they are in the
  245.                     // specialized classes that do implement the getPole and getPrimeMeridianAngle
  246.                     // methods
  247.                     final FieldVector3D<T> pole  = iauPole.getPole(date);
  248.                     final FieldVector3D<T> qNode = iauPole.getNode(date);
  249.                     final FieldRotation<T> rotation =
  250.                                     new FieldRotation<>(pole, qNode, FieldVector3D.getPlusK(field), FieldVector3D.getPlusI(field));

  251.                     // update transform from parent to self
  252.                     return FieldStaticTransform.of(date, position.negate(), rotation);
  253.                 }

  254.             }, frameName == null ? name + INERTIAL_FRAME_SUFFIX : frameName, true);
  255.         }

  256.     }

  257.     /** Body oriented body centered frame. */
  258.     private class BodyOriented extends Frame {

  259.         /**
  260.          * Suffix for body frame name.
  261.          */
  262.         private static final String BODY_FRAME_SUFFIX = "/rotating";

  263.         /**
  264.          * Simple constructor.
  265.          *
  266.          * @param frameName name to use (if null a default name will be built)
  267.          */
  268.         BodyOriented(final String frameName) {
  269.             super(inertialFrame, new TransformProvider() {

  270.                 /** {@inheritDoc} */
  271.                 public Transform getTransform(final AbsoluteDate date) {
  272.                     final double dt = 10.0;
  273.                     final double w0 = iauPole.getPrimeMeridianAngle(date);
  274.                     final double w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  275.                     return new Transform(date,
  276.                             new Rotation(Vector3D.PLUS_K, w0, RotationConvention.FRAME_TRANSFORM),
  277.                             new Vector3D((w1 - w0) / dt, Vector3D.PLUS_K));
  278.                 }

  279.                 /** {@inheritDoc} */
  280.                 public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
  281.                     final double dt = 10.0;
  282.                     final T w0 = iauPole.getPrimeMeridianAngle(date);
  283.                     final T w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  284.                     return new FieldTransform<>(date,
  285.                             new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), w0,
  286.                                     RotationConvention.FRAME_TRANSFORM),
  287.                             new FieldVector3D<>(w1.subtract(w0).divide(dt), Vector3D.PLUS_K));
  288.                 }

  289.             }, frameName == null ? name + BODY_FRAME_SUFFIX : frameName, false);
  290.         }
  291.     }
  292. }