JPLCelestialBody.java

  1. /* Copyright 2002-2022 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 java.io.Serializable;

  19. import org.hipparchus.CalculusFieldElement;
  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.annotation.DefaultDataContext;
  27. import org.orekit.bodies.JPLEphemeridesLoader.EphemerisType;
  28. import org.orekit.data.DataContext;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitInternalError;
  31. import org.orekit.frames.FieldTransform;
  32. import org.orekit.frames.Frame;
  33. import org.orekit.frames.Transform;
  34. import org.orekit.frames.TransformProvider;
  35. import org.orekit.time.AbsoluteDate;
  36. import org.orekit.time.FieldAbsoluteDate;
  37. import org.orekit.utils.FieldPVCoordinates;
  38. import org.orekit.utils.PVCoordinates;
  39. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  40. import org.orekit.utils.TimeStampedPVCoordinates;

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

  45.     /** Serializable UID. */
  46.     private static final long serialVersionUID = 3809787672779740923L;

  47.     /** Name of the body. */
  48.     private final String name;

  49.     /** Regular expression for supported files names. */
  50.     private final String supportedNames;

  51.     /** Ephemeris type to generate. */
  52.     private final JPLEphemeridesLoader.EphemerisType generateType;

  53.     /** Raw position-velocity provider. */
  54.     private final transient JPLEphemeridesLoader.RawPVProvider rawPVProvider;

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

  57.     /** Scaling factor for position-velocity. */
  58.     private final double scale;

  59.     /** IAU pole. */
  60.     private final IAUPole iauPole;

  61.     /** Inertially oriented, body-centered frame. */
  62.     private final Frame inertialFrame;

  63.     /** Body oriented, body-centered frame. */
  64.     private final Frame bodyFrame;

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

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

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

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

  101.         // convert to requested frame
  102.         return transform.transformPVCoordinates(scaledPV);

  103.     }

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

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

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

  117.         // convert to requested frame
  118.         return transform.transformPVCoordinates(scaledPV);

  119.     }

  120.     /** Replace the instance with a data transfer object for serialization.
  121.      * <p>
  122.      * This intermediate class serializes the files supported names, the ephemeris type
  123.      * and the body name.
  124.      * </p>
  125.      * @return data transfer object that will be serialized
  126.      */
  127.     @DefaultDataContext
  128.     private Object writeReplace() {
  129.         return new DTOCelestialBody(supportedNames, generateType, name);
  130.     }

  131.     /** {@inheritDoc} */
  132.     public String getName() {
  133.         return name;
  134.     }

  135.     /** {@inheritDoc} */
  136.     public double getGM() {
  137.         return gm;
  138.     }

  139.     /** {@inheritDoc} */
  140.     public Frame getInertiallyOrientedFrame() {
  141.         return inertialFrame;
  142.     }

  143.     /** {@inheritDoc} */
  144.     public Frame getBodyOrientedFrame() {
  145.         return bodyFrame;
  146.     }

  147.    /** Inertially oriented body centered frame. */
  148.     private class InertiallyOriented extends Frame {

  149.         /** Serializable UID. */
  150.         private static final long serialVersionUID = -8849993808761896559L;

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

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

  159.                 /** Serializable UID. */
  160.                 private static final long serialVersionUID = -8610328386110652400L;

  161.                 /** {@inheritDoc} */
  162.                 public Transform getTransform(final AbsoluteDate date) {

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

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

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

  181.                 }

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

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

  187.                     // compute rotation from ICRF frame to self,
  188.                     // as per the "Report of the IAU/IAG Working Group on Cartographic
  189.                     // Coordinates and Rotational Elements of the Planets and Satellites"
  190.                     // These definitions are common for all recent versions of this report
  191.                     // published every three years, the precise values of pole direction
  192.                     // and W angle coefficients may vary from publication year as models are
  193.                     // adjusted. These coefficients are not in this class, they are in the
  194.                     // specialized classes that do implement the getPole and getPrimeMeridianAngle
  195.                     // methods
  196.                     final FieldVector3D<T> pole  = iauPole.getPole(date);
  197.                     FieldVector3D<T> qNode = FieldVector3D.crossProduct(Vector3D.PLUS_K, pole);
  198.                     if (qNode.getNormSq().getReal() < Precision.SAFE_MIN) {
  199.                         qNode = FieldVector3D.getPlusI(date.getField());
  200.                     }
  201.                     final FieldTransform<T> rotation =
  202.                             new FieldTransform<>(date,
  203.                                                  new FieldRotation<>(pole,
  204.                                                                      qNode,
  205.                                                                      FieldVector3D.getPlusK(date.getField()),
  206.                                                                      FieldVector3D.getPlusI(date.getField())));

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

  209.                 }

  210.             }, frameName == null ? name + INERTIAL_FRAME_SUFFIX : frameName, true);
  211.         }

  212.         /** Replace the instance with a data transfer object for serialization.
  213.          * <p>
  214.          * This intermediate class serializes the files supported names, the ephemeris type
  215.          * and the body name.
  216.          * </p>
  217.          * @return data transfer object that will be serialized
  218.          */
  219.         @DefaultDataContext
  220.         private Object writeReplace() {
  221.             return new DTOInertialFrame(supportedNames, generateType, name);
  222.         }

  223.     }

  224.     /** Body oriented body centered frame. */
  225.     private class BodyOriented extends Frame {

  226.         /** Serializable UID. */
  227.         private static final long serialVersionUID = 20170109L;

  228.         /** Suffix for body frame name. */
  229.         private static final String BODY_FRAME_SUFFIX = "/rotating";

  230.         /** Simple constructor.
  231.          * @param frameName name to use (if null a default name will be built)
  232.          */
  233.         BodyOriented(final String frameName) {
  234.             super(inertialFrame, new TransformProvider() {

  235.                 /** Serializable UID. */
  236.                 private static final long serialVersionUID = 20170109L;

  237.                 /** {@inheritDoc} */
  238.                 public Transform getTransform(final AbsoluteDate date) {
  239.                     final double dt = 10.0;
  240.                     final double w0 = iauPole.getPrimeMeridianAngle(date);
  241.                     final double w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  242.                     return new Transform(date,
  243.                                          new Rotation(Vector3D.PLUS_K, w0, RotationConvention.FRAME_TRANSFORM),
  244.                                          new Vector3D((w1 - w0) / dt, Vector3D.PLUS_K));
  245.                 }

  246.                 /** {@inheritDoc} */
  247.                 public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
  248.                     final double dt = 10.0;
  249.                     final T w0 = iauPole.getPrimeMeridianAngle(date);
  250.                     final T w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  251.                     return new FieldTransform<>(date,
  252.                                                 new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), w0,
  253.                                                                     RotationConvention.FRAME_TRANSFORM),
  254.                                                 new FieldVector3D<>(w1.subtract(w0).divide(dt), Vector3D.PLUS_K));
  255.                 }

  256.             }, frameName == null ? name + BODY_FRAME_SUFFIX : frameName, false);
  257.         }

  258.         /** Replace the instance with a data transfer object for serialization.
  259.          * <p>
  260.          * This intermediate class serializes the files supported names, the ephemeris type
  261.          * and the body name.
  262.          * </p>
  263.          * @return data transfer object that will be serialized
  264.          */
  265.         @DefaultDataContext
  266.         private Object writeReplace() {
  267.             return new DTOBodyFrame(supportedNames, generateType, name);
  268.         }

  269.     }

  270.     /** Internal class used only for serialization. */
  271.     @DefaultDataContext
  272.     private abstract static class DataTransferObject implements Serializable {

  273.         /** Serializable UID. */
  274.         private static final long serialVersionUID = 674742836536072422L;

  275.         /** Regular expression for supported files names. */
  276.         private final String supportedNames;

  277.         /** Ephemeris type to generate. */
  278.         private final EphemerisType generateType;

  279.         /** Name of the body. */
  280.         private final String name;

  281.         /** Simple constructor.
  282.          * @param supportedNames regular expression for supported files names
  283.          * @param generateType ephemeris type to generate
  284.          * @param name name of the body
  285.          */
  286.         DataTransferObject(final String supportedNames, final EphemerisType generateType, final String name) {
  287.             this.supportedNames = supportedNames;
  288.             this.generateType   = generateType;
  289.             this.name           = name;
  290.         }

  291.         /** Get the body associated with the serialized data.
  292.          * @return body associated with the serialized data
  293.          */
  294.         protected JPLCelestialBody getBody() {

  295.             try {
  296.                 // first try to use the factory, in order to avoid building a new instance
  297.                 // each time we deserialize and have the object properly cached
  298.                 final CelestialBody factoryProvided =
  299.                         DataContext.getDefault().getCelestialBodies().getBody(name);
  300.                 if (factoryProvided instanceof JPLCelestialBody) {
  301.                     final JPLCelestialBody jplBody = (JPLCelestialBody) factoryProvided;
  302.                     if (supportedNames.equals(jplBody.supportedNames) && generateType == jplBody.generateType) {
  303.                         // the factory created exactly the object we needed, just return it
  304.                         return jplBody;
  305.                     }
  306.                 }

  307.                 // the factory does not return the object we want
  308.                 // we create a new one from scratch and don't cache it
  309.                 return (JPLCelestialBody) new JPLEphemeridesLoader(supportedNames, generateType).loadCelestialBody(name);

  310.             } catch (OrekitException oe) {
  311.                 throw new OrekitInternalError(oe);
  312.             }

  313.         }

  314.     }

  315.     /** Specialization of the data transfer object for complete celestial body serialization. */
  316.     @DefaultDataContext
  317.     private static class DTOCelestialBody extends DataTransferObject {

  318.         /** Serializable UID. */
  319.         private static final long serialVersionUID = -8287341529741045958L;

  320.         /** Simple constructor.
  321.          * @param supportedNames regular expression for supported files names
  322.          * @param generateType ephemeris type to generate
  323.          * @param name name of the body
  324.          */
  325.         DTOCelestialBody(final String supportedNames, final EphemerisType generateType, final String name) {
  326.             super(supportedNames, generateType, name);
  327.         }

  328.         /** Replace the deserialized data transfer object with a {@link JPLCelestialBody}.
  329.          * @return replacement {@link JPLCelestialBody}
  330.          */
  331.         private Object readResolve() {
  332.             return getBody();
  333.         }

  334.     }

  335.     /** Specialization of the data transfer object for inertially oriented frame serialization. */
  336.     @DefaultDataContext
  337.     private static class DTOInertialFrame extends DataTransferObject {

  338.         /** Serializable UID. */
  339.         private static final long serialVersionUID = 7915071664444154948L;

  340.         /** Simple constructor.
  341.          * @param supportedNames regular expression for supported files names
  342.          * @param generateType ephemeris type to generate
  343.          * @param name name of the body
  344.          */
  345.         DTOInertialFrame(final String supportedNames, final EphemerisType generateType, final String name) {
  346.             super(supportedNames, generateType, name);
  347.         }

  348.         /** Replace the deserialized data transfer object with a {@link Frame}.
  349.          * @return replacement {@link Frame}
  350.          */
  351.         private Object readResolve() {
  352.             return getBody().inertialFrame;
  353.         }

  354.     }

  355.     /** Specialization of the data transfer object for body oriented frame serialization. */
  356.     @DefaultDataContext
  357.     private static class DTOBodyFrame extends DataTransferObject {

  358.         /** Serializable UID. */
  359.         private static final long serialVersionUID = -3194195019557081000L;

  360.         /** Simple constructor.
  361.          * @param supportedNames regular expression for supported files names
  362.          * @param generateType ephemeris type to generate
  363.          * @param name name of the body
  364.          */
  365.         DTOBodyFrame(final String supportedNames, final EphemerisType generateType, final String name) {
  366.             super(supportedNames, generateType, name);
  367.         }

  368.         /** Replace the deserialized data transfer object with a {@link Frame}.
  369.          * @return replacement {@link Frame}
  370.          */
  371.         private Object readResolve() {
  372.             return getBody().bodyFrame;
  373.         }

  374.     }

  375. }