JPLCelestialBody.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.RealFieldElement;
  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.bodies.JPLEphemeridesLoader.EphemerisType;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.frames.FieldTransform;
  30. import org.orekit.frames.Frame;
  31. import org.orekit.frames.Transform;
  32. import org.orekit.frames.TransformProvider;
  33. import org.orekit.time.AbsoluteDate;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.utils.FieldPVCoordinates;
  36. import org.orekit.utils.PVCoordinates;
  37. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  38. import org.orekit.utils.TimeStampedPVCoordinates;

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

  43.     /** Serializable UID. */
  44.     private static final long serialVersionUID = 3809787672779740923L;

  45.     /** Name of the body. */
  46.     private final String name;

  47.     /** Regular expression for supported files names. */
  48.     private final String supportedNames;

  49.     /** Ephemeris type to generate. */
  50.     private final JPLEphemeridesLoader.EphemerisType generateType;

  51.     /** Raw position-velocity provider. */
  52.     private final transient JPLEphemeridesLoader.RawPVProvider rawPVProvider;

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

  55.     /** Scaling factor for position-velocity. */
  56.     private final double scale;

  57.     /** IAU pole. */
  58.     private final IAUPole iauPole;

  59.     /** Inertially oriented, body-centered frame. */
  60.     private final Frame inertialFrame;

  61.     /** Body oriented, body-centered frame. */
  62.     private final Frame bodyFrame;

  63.     /** Build an instance and the underlying frame.
  64.      * @param name name of the body
  65.      * @param supportedNames regular expression for supported files names
  66.      * @param generateType ephemeris type to generate
  67.      * @param rawPVProvider raw position-velocity provider
  68.      * @param gm attraction coefficient (in m³/s²)
  69.      * @param scale scaling factor for position-velocity
  70.      * @param iauPole IAU pole implementation
  71.      * @param definingFrameAlignedWithICRF frame in which celestial body coordinates are defined,
  72.      * this frame <strong>must</strong> be aligned with ICRF
  73.      */
  74.     JPLCelestialBody(final String name, final String supportedNames,
  75.                      final JPLEphemeridesLoader.EphemerisType generateType,
  76.                      final JPLEphemeridesLoader.RawPVProvider rawPVProvider,
  77.                      final double gm, final double scale,
  78.                      final IAUPole iauPole, final Frame definingFrameAlignedWithICRF) {
  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);
  87.         this.bodyFrame      = new BodyOriented();
  88.     }

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

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

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

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

  99.     }

  100.     /** Get the {@link FieldPVCoordinates} of the body in the selected frame.
  101.      * @param date current date
  102.      * @param frame the frame where to define the position
  103.      * @param <T> type fo the field elements
  104.      * @return time-stamped position/velocity of the body (m and m/s)
  105.      * @exception OrekitException if position cannot be computed in given frame
  106.      */
  107.     public <T extends RealFieldElement<T>> TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date,
  108.                                                                                              final Frame frame)
  109.         throws OrekitException {

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

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

  115.         // convert to requested frame
  116.         return transform.transformPVCoordinates(scaledPV);

  117.     }

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

  128.     /** {@inheritDoc} */
  129.     public String getName() {
  130.         return name;
  131.     }

  132.     /** {@inheritDoc} */
  133.     public double getGM() {
  134.         return gm;
  135.     }

  136.     /** {@inheritDoc} */
  137.     public Frame getInertiallyOrientedFrame() {
  138.         return inertialFrame;
  139.     }

  140.     /** {@inheritDoc} */
  141.     public Frame getBodyOrientedFrame() {
  142.         return bodyFrame;
  143.     }

  144.    /** Inertially oriented body centered frame. */
  145.     private class InertiallyOriented extends Frame {

  146.         /** Serializable UID. */
  147.         private static final long serialVersionUID = -8849993808761896559L;

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

  150.         /** Simple constructor.
  151.          * @param definingFrame frame in which celestial body coordinates are defined
  152.          */
  153.         InertiallyOriented(final Frame definingFrame) {
  154.             super(definingFrame, new TransformProvider() {

  155.                 /** Serializable UID. */
  156.                 private static final long serialVersionUID = -8610328386110652400L;

  157.                 /** {@inheritDoc} */
  158.                 public Transform getTransform(final AbsoluteDate date) throws OrekitException {

  159.                     // compute translation from parent frame to self
  160.                     final PVCoordinates pv = getPVCoordinates(date, definingFrame);
  161.                     final Transform translation = new Transform(date, pv.negate());

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

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

  177.                 }

  178.                 /** {@inheritDoc} */
  179.                 public <T extends RealFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date)
  180.                     throws OrekitException {

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

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

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

  206.                 }

  207.             }, name + INERTIAL_FRAME_SUFFIX, true);
  208.         }

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

  219.     }

  220.     /** Body oriented body centered frame. */
  221.     private class BodyOriented extends Frame {

  222.         /** Serializable UID. */
  223.         private static final long serialVersionUID = 20170109L;

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

  226.         /** Simple constructor.
  227.          */
  228.         BodyOriented() {
  229.             super(inertialFrame, new TransformProvider() {

  230.                 /** Serializable UID. */
  231.                 private static final long serialVersionUID = 20170109L;

  232.                 /** {@inheritDoc} */
  233.                 public Transform getTransform(final AbsoluteDate date) throws OrekitException {
  234.                     final double dt = 10.0;
  235.                     final double w0 = iauPole.getPrimeMeridianAngle(date);
  236.                     final double w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  237.                     return new Transform(date,
  238.                                          new Rotation(Vector3D.PLUS_K, w0, RotationConvention.FRAME_TRANSFORM),
  239.                                          new Vector3D((w1 - w0) / dt, Vector3D.PLUS_K));
  240.                 }

  241.                 /** {@inheritDoc} */
  242.                 public <T extends RealFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date)
  243.                     throws OrekitException {
  244.                     final double dt = 10.0;
  245.                     final T w0 = iauPole.getPrimeMeridianAngle(date);
  246.                     final T w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  247.                     return new FieldTransform<>(date,
  248.                                                 new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), w0,
  249.                                                                     RotationConvention.FRAME_TRANSFORM),
  250.                                                 new FieldVector3D<>(w1.subtract(w0).divide(dt), Vector3D.PLUS_K));
  251.                 }

  252.             }, name + BODY_FRAME_SUFFIX, false);
  253.         }

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

  264.     }

  265.     /** Internal class used only for serialization. */
  266.     private abstract static class DataTransferObject implements Serializable {

  267.         /** Serializable UID. */
  268.         private static final long serialVersionUID = 674742836536072422L;

  269.         /** Regular expression for supported files names. */
  270.         private final String supportedNames;

  271.         /** Ephemeris type to generate. */
  272.         private final EphemerisType generateType;

  273.         /** Name of the body. */
  274.         private final String name;

  275.         /** Simple constructor.
  276.          * @param supportedNames regular expression for supported files names
  277.          * @param generateType ephemeris type to generate
  278.          * @param name name of the body
  279.          */
  280.         DataTransferObject(final String supportedNames, final EphemerisType generateType, final String name) {
  281.             this.supportedNames = supportedNames;
  282.             this.generateType   = generateType;
  283.             this.name           = name;
  284.         }

  285.         /** Get the body associated with the serialized data.
  286.          * @return body associated with the serialized data
  287.          */
  288.         protected JPLCelestialBody getBody() {

  289.             try {
  290.                 // first try to use the factory, in order to avoid building a new instance
  291.                 // each time we deserialize and have the object properly cached
  292.                 final CelestialBody factoryProvided = CelestialBodyFactory.getBody(name);
  293.                 if (factoryProvided instanceof JPLCelestialBody) {
  294.                     final JPLCelestialBody jplBody = (JPLCelestialBody) factoryProvided;
  295.                     if (supportedNames.equals(jplBody.supportedNames) && generateType == jplBody.generateType) {
  296.                         // the factory created exactly the object we needed, just return it
  297.                         return jplBody;
  298.                     }
  299.                 }

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

  303.             } catch (OrekitException oe) {
  304.                 throw new OrekitInternalError(oe);
  305.             }

  306.         }

  307.     }

  308.     /** Specialization of the data transfer object for complete celestial body serialization. */
  309.     private static class DTOCelestialBody extends DataTransferObject {

  310.         /** Serializable UID. */
  311.         private static final long serialVersionUID = -8287341529741045958L;

  312.         /** Simple constructor.
  313.          * @param supportedNames regular expression for supported files names
  314.          * @param generateType ephemeris type to generate
  315.          * @param name name of the body
  316.          */
  317.         DTOCelestialBody(final String supportedNames, final EphemerisType generateType, final String name) {
  318.             super(supportedNames, generateType, name);
  319.         }

  320.         /** Replace the deserialized data transfer object with a {@link JPLCelestialBody}.
  321.          * @return replacement {@link JPLCelestialBody}
  322.          */
  323.         private Object readResolve() {
  324.             return getBody();
  325.         }

  326.     }

  327.     /** Specialization of the data transfer object for inertially oriented frame serialization. */
  328.     private static class DTOInertialFrame extends DataTransferObject {

  329.         /** Serializable UID. */
  330.         private static final long serialVersionUID = 7915071664444154948L;

  331.         /** Simple constructor.
  332.          * @param supportedNames regular expression for supported files names
  333.          * @param generateType ephemeris type to generate
  334.          * @param name name of the body
  335.          */
  336.         DTOInertialFrame(final String supportedNames, final EphemerisType generateType, final String name) {
  337.             super(supportedNames, generateType, name);
  338.         }

  339.         /** Replace the deserialized data transfer object with a {@link Frame}.
  340.          * @return replacement {@link Frame}
  341.          */
  342.         private Object readResolve() {
  343.             return getBody().inertialFrame;
  344.         }

  345.     }

  346.     /** Specialization of the data transfer object for body oriented frame serialization. */
  347.     private static class DTOBodyFrame extends DataTransferObject {

  348.         /** Serializable UID. */
  349.         private static final long serialVersionUID = -3194195019557081000L;

  350.         /** Simple constructor.
  351.          * @param supportedNames regular expression for supported files names
  352.          * @param generateType ephemeris type to generate
  353.          * @param name name of the body
  354.          */
  355.         DTOBodyFrame(final String supportedNames, final EphemerisType generateType, final String name) {
  356.             super(supportedNames, generateType, name);
  357.         }

  358.         /** Replace the deserialized data transfer object with a {@link Frame}.
  359.          * @return replacement {@link Frame}
  360.          */
  361.         private Object readResolve() {
  362.             return getBody().bodyFrame;
  363.         }

  364.     }

  365. }