JPLCelestialBody.java

  1. /* Copyright 2002-2013 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.apache.commons.math3.geometry.euclidean.threed.Rotation;
  20. import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  21. import org.apache.commons.math3.util.Precision;
  22. import org.orekit.bodies.JPLEphemeridesLoader.EphemerisType;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.frames.Frame;
  25. import org.orekit.frames.Transform;
  26. import org.orekit.frames.TransformProvider;
  27. import org.orekit.time.AbsoluteDate;
  28. import org.orekit.utils.PVCoordinates;

  29. /** Implementation of the {@link CelestialBody} interface using JPL or INPOP ephemerides.
  30.  * @author Luc Maisonobe
  31.  */
  32. class JPLCelestialBody implements CelestialBody {

  33.     /** Serializable UID. */
  34.     private static final long serialVersionUID = 3809787672779740923L;

  35.     /** Name of the body. */
  36.     private final String name;

  37.     /** Regular expression for supported files names. */
  38.     private final String supportedNames;

  39.     /** Ephemeris type to generate. */
  40.     private final JPLEphemeridesLoader.EphemerisType generateType;

  41.     /** Raw position-velocity provider. */
  42.     private final transient JPLEphemeridesLoader.RawPVProvider rawPVProvider;

  43.     /** Attraction coefficient of the body (m<sup>3</sup>/s<sup>2</sup>). */
  44.     private final double gm;

  45.     /** Scaling factor for position-velocity. */
  46.     private final double scale;

  47.     /** IAU pole. */
  48.     private final IAUPole iauPole;

  49.     /** Inertially oriented, body-centered frame. */
  50.     private final Frame inertialFrame;

  51.     /** Body oriented, body-centered frame. */
  52.     private final Frame bodyFrame;

  53.     /** Build an instance and the underlying frame.
  54.      * @param name name of the body
  55.      * @param supportedNames regular expression for supported files names
  56.      * @param generateType ephemeris type to generate
  57.      * @param rawPVProvider raw position-velocity provider
  58.      * @param gm attraction coefficient (in m<sup>3</sup>/s<sup>2</sup>)
  59.      * @param scale scaling factor for position-velocity
  60.      * @param iauPole IAU pole implementation
  61.      * @param definingFrameAlignedWithICRF frame in which celestial body coordinates are defined,
  62.      * this frame <strong>must</strong> be aligned with ICRF
  63.      */
  64.     public JPLCelestialBody(final String name, final String supportedNames,
  65.                             final JPLEphemeridesLoader.EphemerisType generateType,
  66.                             final JPLEphemeridesLoader.RawPVProvider rawPVProvider,
  67.                             final double gm, final double scale,
  68.                             final IAUPole iauPole, final Frame definingFrameAlignedWithICRF) {
  69.         this.name           = name;
  70.         this.gm             = gm;
  71.         this.scale          = scale;
  72.         this.supportedNames = supportedNames;
  73.         this.generateType   = generateType;
  74.         this.rawPVProvider  = rawPVProvider;
  75.         this.iauPole        = iauPole;
  76.         this.inertialFrame  = new InertiallyOriented(definingFrameAlignedWithICRF);
  77.         this.bodyFrame      = new BodyOriented();
  78.     }

  79.     /** {@inheritDoc} */
  80.     public PVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
  81.         throws OrekitException {

  82.         // get raw position-velocity
  83.         final PVCoordinates pv = rawPVProvider.getRawPV(date);

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

  86.         // apply the scale factor
  87.         return new PVCoordinates(scale, transform.transformPVCoordinates(pv));

  88.     }

  89.     /** Replace the instance with a data transfer object for serialization.
  90.      * <p>
  91.      * This intermediate class serializes the files supported names, the ephemeris type
  92.      * and the body name.
  93.      * </p>
  94.      * @return data transfer object that will be serialized
  95.      */
  96.     private Object writeReplace() {
  97.         return new DTOCelestialBody(supportedNames, generateType, name);
  98.     }

  99.     /** {@inheritDoc} */
  100.     public String getName() {
  101.         return name;
  102.     }

  103.     /** {@inheritDoc} */
  104.     public double getGM() {
  105.         return gm;
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Deprecated
  109.     public Frame getFrame() {
  110.         return inertialFrame;
  111.     }

  112.     /** {@inheritDoc} */
  113.     public Frame getInertiallyOrientedFrame() {
  114.         return inertialFrame;
  115.     }

  116.     /** {@inheritDoc} */
  117.     public Frame getBodyOrientedFrame() {
  118.         return bodyFrame;
  119.     }

  120.    /** Inertially oriented body centered frame. */
  121.     private class InertiallyOriented extends Frame {

  122.         /** Serializable UID. */
  123.         private static final long serialVersionUID = -8849993808761896559L;

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

  126.         /** Simple constructor.
  127.          * @param definingFrame frame in which celestial body coordinates are defined
  128.          */
  129.         public InertiallyOriented(final Frame definingFrame) {
  130.             super(definingFrame, new TransformProvider() {

  131.                 /** Serializable UID. */
  132.                 private static final long serialVersionUID = -8610328386110652400L;

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

  135.                     // compute translation from parent frame to self
  136.                     final PVCoordinates pv = getPVCoordinates(date, definingFrame);
  137.                     final Transform translation = new Transform(date, pv.negate());

  138.                     // compute rotation from ICRF frame to self,
  139.                     // as per the "Report of the IAU/IAG Working Group on Cartographic
  140.                     // Coordinates and Rotational Elements of the Planets and Satellites"
  141.                     // These definitions are common for all recent versions of this report
  142.                     // published every three years, the precise values of pole direction
  143.                     // and W angle coefficients may vary from publication year as models are
  144.                     // adjusted. These coefficients are not in this class, they are in the
  145.                     // specialized classes that do implement the getPole and getPrimeMeridianAngle
  146.                     // methods
  147.                     final Vector3D pole  = iauPole.getPole(date);
  148.                     Vector3D qNode = Vector3D.crossProduct(Vector3D.PLUS_K, pole);
  149.                     if (qNode.getNormSq() < Precision.SAFE_MIN) {
  150.                         qNode = Vector3D.PLUS_I;
  151.                     }
  152.                     final Transform rotation =
  153.                             new Transform(date, new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I));

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

  156.                 }

  157.             }, name + INERTIAL_FRAME_SUFFIX, true);
  158.         }

  159.         /** Replace the instance with a data transfer object for serialization.
  160.          * <p>
  161.          * This intermediate class serializes the files supported names, the ephemeris type
  162.          * and the body name.
  163.          * </p>
  164.          * @return data transfer object that will be serialized
  165.          */
  166.         private Object writeReplace() {
  167.             return new DTOInertialFrame(supportedNames, generateType, name);
  168.         }

  169.     }

  170.     /** Body oriented body centered frame. */
  171.     private class BodyOriented extends Frame {

  172.         /** Serializable UID. */
  173.         private static final long serialVersionUID = -2286454820784307274L;

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

  176.         /** Simple constructor.
  177.          */
  178.         public BodyOriented() {
  179.             super(inertialFrame, new TransformProvider() {

  180.                 /** Serializable UID. */
  181.                 private static final long serialVersionUID = 5973062576520917181L;

  182.                 /** {@inheritDoc} */
  183.                 public Transform getTransform(final AbsoluteDate date) throws OrekitException {
  184.                     final double dt = 10.0;
  185.                     final double w0 = iauPole.getPrimeMeridianAngle(date);
  186.                     final double w1 = iauPole.getPrimeMeridianAngle(date.shiftedBy(dt));
  187.                     return new Transform(date,
  188.                                          new Rotation(Vector3D.PLUS_K, -w0),
  189.                                          new Vector3D((w1 - w0) / dt, Vector3D.PLUS_K));
  190.                 }

  191.             }, name + BODY_FRAME_SUFFIX, false);
  192.         };

  193.         /** Replace the instance with a data transfer object for serialization.
  194.          * <p>
  195.          * This intermediate class serializes the files supported names, the ephemeris type
  196.          * and the body name.
  197.          * </p>
  198.          * @return data transfer object that will be serialized
  199.          */
  200.         private Object writeReplace() {
  201.             return new DTOBodyFrame(supportedNames, generateType, name);
  202.         }

  203.     }

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

  206.         /** Serializable UID. */
  207.         private static final long serialVersionUID = 674742836536072422L;

  208.         /** Regular expression for supported files names. */
  209.         private final String supportedNames;

  210.         /** Ephemeris type to generate. */
  211.         private final EphemerisType generateType;

  212.         /** Name of the body. */
  213.         private final String name;

  214.         /** Simple constructor.
  215.          * @param supportedNames regular expression for supported files names
  216.          * @param generateType ephemeris type to generate
  217.          * @param name name of the body
  218.          */
  219.         public DataTransferObject(final String supportedNames, final EphemerisType generateType, final String name) {
  220.             this.supportedNames = supportedNames;
  221.             this.generateType   = generateType;
  222.             this.name           = name;
  223.         }

  224.         /** Get the body associated with the serialized data.
  225.          * @return body associated with the serialized data
  226.          */
  227.         protected JPLCelestialBody getBody() {

  228.             try {
  229.                 // first try to use the factory, in order to avoid building a new instance
  230.                 // each time we deserialize and have the object properly cached
  231.                 final CelestialBody factoryProvided = CelestialBodyFactory.getBody(name);
  232.                 if (factoryProvided instanceof JPLCelestialBody) {
  233.                     final JPLCelestialBody jplBody = (JPLCelestialBody) factoryProvided;
  234.                     if (supportedNames.equals(jplBody.supportedNames) && generateType == jplBody.generateType) {
  235.                         // the factory created exactly the object we needed, just return it
  236.                         return jplBody;
  237.                     }
  238.                 }

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

  242.             } catch (OrekitException oe) {
  243.                 throw OrekitException.createInternalError(oe);
  244.             }

  245.         }

  246.     }

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

  249.         /** Serializable UID. */
  250.         private static final long serialVersionUID = -8287341529741045958L;

  251.         /** Simple constructor.
  252.          * @param supportedNames regular expression for supported files names
  253.          * @param generateType ephemeris type to generate
  254.          * @param name name of the body
  255.          */
  256.         public DTOCelestialBody(final String supportedNames, final EphemerisType generateType, final String name) {
  257.             super(supportedNames, generateType, name);
  258.         }

  259.         /** Replace the deserialized data transfer object with a {@link JPLCelestialBody}.
  260.          * @return replacement {@link JPLCelestialBody}
  261.          */
  262.         private Object readResolve() {
  263.             return getBody();
  264.         }

  265.     }

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

  268.         /** Serializable UID. */
  269.         private static final long serialVersionUID = 7915071664444154948L;

  270.         /** Simple constructor.
  271.          * @param supportedNames regular expression for supported files names
  272.          * @param generateType ephemeris type to generate
  273.          * @param name name of the body
  274.          */
  275.         public DTOInertialFrame(final String supportedNames, final EphemerisType generateType, final String name) {
  276.             super(supportedNames, generateType, name);
  277.         }

  278.         /** Replace the deserialized data transfer object with a {@link Frame}.
  279.          * @return replacement {@link Frame}
  280.          */
  281.         private Object readResolve() {
  282.             return getBody().inertialFrame;
  283.         }

  284.     }

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

  287.         /** Serializable UID. */
  288.         private static final long serialVersionUID = -3194195019557081000L;

  289.         /** Simple constructor.
  290.          * @param supportedNames regular expression for supported files names
  291.          * @param generateType ephemeris type to generate
  292.          * @param name name of the body
  293.          */
  294.         public DTOBodyFrame(final String supportedNames, final EphemerisType generateType, final String name) {
  295.             super(supportedNames, generateType, name);
  296.         }

  297.         /** Replace the deserialized data transfer object with a {@link Frame}.
  298.          * @return replacement {@link Frame}
  299.          */
  300.         private Object readResolve() {
  301.             return getBody().bodyFrame;
  302.         }

  303.     }

  304. }