DSSTPropagator.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.propagation.semianalytical.dsst;

  18. import java.io.NotSerializableException;
  19. import java.io.Serializable;
  20. import java.util.ArrayList;
  21. import java.util.Arrays;
  22. import java.util.List;

  23. import org.apache.commons.math3.ode.AbstractIntegrator;
  24. import org.apache.commons.math3.ode.nonstiff.AdaptiveStepsizeIntegrator;
  25. import org.apache.commons.math3.ode.nonstiff.DormandPrince853Integrator;
  26. import org.orekit.attitudes.Attitude;
  27. import org.orekit.attitudes.AttitudeProvider;
  28. import org.orekit.bodies.CelestialBody;
  29. import org.orekit.bodies.CelestialBodyFactory;
  30. import org.orekit.errors.OrekitException;
  31. import org.orekit.errors.OrekitMessages;
  32. import org.orekit.errors.PropagationException;
  33. import org.orekit.forces.ForceModel;
  34. import org.orekit.forces.SphericalSpacecraft;
  35. import org.orekit.forces.drag.Atmosphere;
  36. import org.orekit.forces.drag.DragForce;
  37. import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
  38. import org.orekit.forces.gravity.ThirdBodyAttraction;
  39. import org.orekit.forces.gravity.potential.GravityFieldFactory;
  40. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  41. import org.orekit.forces.radiation.SolarRadiationPressure;
  42. import org.orekit.frames.Frame;
  43. import org.orekit.frames.FramesFactory;
  44. import org.orekit.orbits.EquinoctialOrbit;
  45. import org.orekit.orbits.Orbit;
  46. import org.orekit.orbits.OrbitType;
  47. import org.orekit.orbits.PositionAngle;
  48. import org.orekit.propagation.Propagator;
  49. import org.orekit.propagation.SpacecraftState;
  50. import org.orekit.propagation.conversion.OsculatingToMeanElementsConverter;
  51. import org.orekit.propagation.events.EventDetector;
  52. import org.orekit.propagation.integration.AbstractIntegratedPropagator;
  53. import org.orekit.propagation.integration.StateMapper;
  54. import org.orekit.propagation.numerical.NumericalPropagator;
  55. import org.orekit.propagation.semianalytical.dsst.forces.DSSTAtmosphericDrag;
  56. import org.orekit.propagation.semianalytical.dsst.forces.DSSTCentralBody;
  57. import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
  58. import org.orekit.propagation.semianalytical.dsst.forces.DSSTSolarRadiationPressure;
  59. import org.orekit.propagation.semianalytical.dsst.forces.DSSTThirdBody;
  60. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  61. import org.orekit.time.AbsoluteDate;
  62. import org.orekit.utils.IERSConventions;

  63. /**
  64.  * This class propagates {@link org.orekit.orbits.Orbit orbits} using the DSST theory.
  65.  * <p>
  66.  * Whereas analytical propagators are configured only thanks to their various
  67.  * constructors and can be used immediately after construction, such a semianalytical
  68.  * propagator configuration involves setting several parameters between construction
  69.  * time and propagation time, just as numerical propagators.
  70.  * </p>
  71.  * <p>
  72.  * The configuration parameters that can be set are:
  73.  * </p>
  74.  * <ul>
  75.  * <li>the initial spacecraft state ({@link #setInitialState(SpacecraftState)})</li>
  76.  * <li>the various force models ({@link #addForceModel(DSSTForceModel)},
  77.  * {@link #removeForceModels()})</li>
  78.  * <li>the discrete events that should be triggered during propagation (
  79.  * {@link #addEventDetector(org.orekit.propagation.events.EventDetector)},
  80.  * {@link #clearEventsDetectors()})</li>
  81.  * <li>the binding logic with the rest of the application ({@link #setSlaveMode()},
  82.  * {@link #setMasterMode(double, org.orekit.propagation.sampling.OrekitFixedStepHandler)},
  83.  * {@link #setMasterMode(org.orekit.propagation.sampling.OrekitStepHandler)},
  84.  * {@link #setEphemerisMode()}, {@link #getGeneratedEphemeris()})</li>
  85.  * </ul>
  86.  * <p>
  87.  * From these configuration parameters, only the initial state is mandatory.
  88.  * The default propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial}
  89.  * parameters with {@link PositionAngle#TRUE true} longitude argument.
  90.  * The central attraction coefficient used to define the initial orbit will be used.
  91.  * However, specifying only the initial state would mean the propagator would use
  92.  * only keplerian forces. In this case, the simpler
  93.  * {@link org.orekit.propagation.analytical.KeplerianPropagator KeplerianPropagator}
  94.  * class would be more effective.
  95.  * </p>
  96.  * <p>
  97.  * The underlying numerical integrator set up in the constructor may also have
  98.  * its own configuration parameters. Typical configuration parameters for adaptive
  99.  * stepsize integrators are the min, max and perhaps start step size as well as
  100.  * the absolute and/or relative errors thresholds.
  101.  * </p>
  102.  * <p>
  103.  * The state that is seen by the integrator is a simple six elements double array.
  104.  * These six elements are:
  105.  * <ul>
  106.  * <li>the {@link org.orekit.orbits.EquinoctialOrbit equinoctial orbit parameters}
  107.  * (a, e<sub>x</sub>, e<sub>y</sub>, h<sub>x</sub>, h<sub>y</sub>, &lambda;<sub>m</sub>)
  108.  * in meters and radians,</li>
  109.  * </ul>
  110.  * </p>
  111.  * <p>
  112.  * The same propagator can be reused for several orbit extrapolations,
  113.  * by resetting the initial state without modifying the other configuration
  114.  * parameters. However, the same instance cannot be used simultaneously by
  115.  * different threads, the class is <em>not</em> thread-safe.
  116.  * </p>
  117.  *
  118.  * @see SpacecraftState
  119.  * @see DSSTForceModel
  120.  * @author Romain Di Costanzo
  121.  * @author Pascal Parraud
  122.  */
  123. public class DSSTPropagator extends AbstractIntegratedPropagator {

  124.     /** Retrograde factor I.
  125.      *  <p>
  126.      *  DSST model needs equinoctial orbit as internal representation.
  127.      *  Classical equinoctial elements have discontinuities when inclination
  128.      *  is close to zero. In this representation, I = +1. <br>
  129.      *  To avoid this discontinuity, another representation exists and equinoctial
  130.      *  elements can be expressed in a different way, called "retrograde" orbit.
  131.      *  This implies I = -1. <br>
  132.      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  133.      *  But for the sake of consistency with the theory, the retrograde factor
  134.      *  has been kept in the formulas.
  135.      *  </p>
  136.      */
  137.     private static final int I = 1;

  138.     /** State mapper holding the force models. */
  139.     private MeanPlusShortPeriodicMapper mapper;

  140.     /** Create a new instance of DSSTPropagator.
  141.      *  <p>
  142.      *  After creation, there are no perturbing forces at all.
  143.      *  This means that if {@link #addForceModel addForceModel}
  144.      *  is not called after creation, the integrated orbit will
  145.      *  follow a keplerian evolution only.
  146.      *  </p>
  147.      *  @param integrator numerical integrator to use for propagation.
  148.      */
  149.     public DSSTPropagator(final AbstractIntegrator integrator) {
  150.         super(integrator);
  151.         initMapper();
  152.         // DSST uses only equinoctial orbits and mean longitude argument
  153.         setOrbitType(OrbitType.EQUINOCTIAL);
  154.         setPositionAngleType(PositionAngle.TRUE);
  155.         setAttitudeProvider(DEFAULT_LAW);
  156.     }

  157.     /** Set the initial state with osculating orbital elements.
  158.      *  @param initialState initial state (defined with osculating elements)
  159.      *  @throws PropagationException if the initial state cannot be set
  160.      */
  161.     public void setInitialState(final SpacecraftState initialState)
  162.         throws PropagationException {
  163.         setInitialState(initialState, true);
  164.     }

  165.     /** Set the initial state.
  166.      *  @param initialState initial state
  167.      *  @param withOsculatingElements true if the orbital state is defined with osculating elements
  168.      *  @throws PropagationException if the initial state cannot be set
  169.      */
  170.     public void setInitialState(final SpacecraftState initialState,
  171.                                 final boolean withOsculatingElements)
  172.         throws PropagationException {
  173.         mapper.setInitialIsOsculating(withOsculatingElements);
  174.         resetInitialState(initialState);
  175.     }

  176.     /** Reset the initial state.
  177.      *
  178.      *  @param state new initial state
  179.      *  @throws PropagationException if initial state cannot be reset
  180.      */
  181.     public void resetInitialState(final SpacecraftState state) throws PropagationException {
  182.         super.setStartDate(state.getDate());
  183.         super.resetInitialState(state);
  184.     }

  185.     /** Check if the initial state is provided in osculating elements.
  186.      * @return true if initial state is provided in osculating elements
  187.      */
  188.     public boolean initialIsOsculating() {
  189.         return mapper.initialIsOsculating();
  190.     }

  191.     /** Add a force model to the global perturbation model.
  192.      *  <p>
  193.      *  If this method is not called at all,
  194.      *  the integrated orbit will follow a keplerian evolution only.
  195.      *  </p>
  196.      *  @param force perturbing {@link DSSTForceModel force} to add
  197.      *  @see #removeForceModels()
  198.      */
  199.     public void addForceModel(final DSSTForceModel force) {
  200.         mapper.addForceModel(force);
  201.     }

  202.     /** Remove all perturbing force models from the global perturbation model.
  203.      *  <p>
  204.      *  Once all perturbing forces have been removed (and as long as no new force model is added),
  205.      *  the integrated orbit will follow a keplerian evolution only.
  206.      *  </p>
  207.      *  @see #addForceModel(DSSTForceModel)
  208.      */
  209.     public void removeForceModels() {
  210.         mapper.removeForceModels();
  211.     }

  212.     /** Override the default value of the parameter.
  213.      *  <p>
  214.      *  By default, if the initial orbit is defined as osculating,
  215.      *  it will be averaged over 2 satellite revolutions.
  216.      *  This can be changed by using this method.
  217.      *  </p>
  218.      *  @param satelliteRevolution number of satellite revolutions to use for converting osculating to mean
  219.      *                             elements
  220.      */
  221.     public void setSatelliteRevolution(final int satelliteRevolution) {
  222.         mapper.setSatelliteRevolution(satelliteRevolution);
  223.     }

  224.     /** Get the number of satellite revolutions to use for converting osculating to mean elements.
  225.      *  @return number of satellite revolutions to use for converting osculating to mean elements
  226.      */
  227.     public int getSatelliteRevolution() {
  228.         return mapper.getSatelliteRevolution();
  229.     }

  230.     /** Method called just before integration.
  231.      * <p>
  232.      * The default implementation does nothing, it may be specialized in subclasses.
  233.      * </p>
  234.      * @param initialState initial state
  235.      * @param tEnd target date at which state should be propagated
  236.      * @exception OrekitException if hook cannot be run
  237.      */
  238.     protected void beforeIntegration(final SpacecraftState initialState,
  239.                                      final AbsoluteDate tEnd)
  240.         throws OrekitException {

  241.         // compute common auxiliary elements
  242.         final AuxiliaryElements aux = new AuxiliaryElements(initialState.getOrbit(), I);

  243.         // initialize all perturbing forces
  244.         for (final DSSTForceModel force : mapper.getForceModels()) {
  245.             force.initialize(aux);
  246.         }

  247.     }

  248.     /** {@inheritDoc} */
  249.     protected StateMapper createMapper(final AbsoluteDate referenceDate, final double mu,
  250.                                        final OrbitType orbitType, final PositionAngle positionAngleType,
  251.                                        final AttitudeProvider attitudeProvider, final Frame frame) {

  252.         // create a mapper with the common settings provided as arguments
  253.         final MeanPlusShortPeriodicMapper newMapper =
  254.                 new MeanPlusShortPeriodicMapper(referenceDate, mu, attitudeProvider, frame);

  255.         // copy the specific settings from the existing mapper
  256.         if (mapper != null) {
  257.             for (final DSSTForceModel forceModel : mapper.getForceModels()) {
  258.                 newMapper.addForceModel(forceModel);
  259.             }
  260.             newMapper.setSatelliteRevolution(mapper.getSatelliteRevolution());
  261.             newMapper.setInitialIsOsculating(mapper.initialIsOsculating());
  262.         }

  263.         mapper = newMapper;
  264.         return mapper;

  265.     }

  266.     /** Internal mapper using mean parameters plus short periodic terms. */
  267.     private static class MeanPlusShortPeriodicMapper extends StateMapper implements Serializable {

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

  270.         /** Flag specifying whether the initial orbital state is given with osculating elements. */
  271.         private boolean                    initialIsOsculating;

  272.         /** Force models used to compute short periodic terms. */
  273.         private final transient List<DSSTForceModel> forceModels;

  274.         /** Number of satellite revolutions in the averaging interval. */
  275.         private int                        satelliteRevolution;

  276.         /** Simple constructor.
  277.          * <p>
  278.          * The position parameter type is meaningful only if {@link
  279.          * #getOrbitType() propagation orbit type}
  280.          * support it. As an example, it is not meaningful for propagation
  281.          * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  282.          * </p>
  283.          * @param referenceDate reference date
  284.          * @param mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  285.          * @param attitudeProvider attitude provider
  286.          * @param frame inertial frame
  287.          */
  288.         public MeanPlusShortPeriodicMapper(final AbsoluteDate referenceDate, final double mu,
  289.                                            final AttitudeProvider attitudeProvider, final Frame frame) {

  290.             super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngle.MEAN, attitudeProvider, frame);

  291.             this.forceModels = new ArrayList<DSSTForceModel>();

  292.             // Default averaging period for conversion from osculating to mean elements
  293.             this.satelliteRevolution = 2;

  294.             this.initialIsOsculating = true;

  295.         }

  296.         /** {@inheritDoc} */
  297.         public SpacecraftState mapArrayToState(final double t, final double[] y)
  298.             throws OrekitException {

  299.             final AbsoluteDate date = mapDoubleToDate(t);

  300.             // add short periodic variations to mean elements to get osculating elements
  301.             // (the loop may not be performed if there are no force models, in the
  302.             //  case we want to remain in mean parameters only)
  303.             final double[] osculatingElements = y.clone();
  304.             for (final DSSTForceModel forceModel : forceModels) {
  305.                 final double[] shortPeriodic = forceModel.getShortPeriodicVariations(date, y);
  306.                 for (int i = 0; i < shortPeriodic.length; i++) {
  307.                     osculatingElements[i] += shortPeriodic[i];
  308.                 }
  309.             }

  310.             final double mass = y[6];
  311.             if (mass <= 0.0) {
  312.                 throw new PropagationException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE, mass);
  313.             }

  314.             final Orbit orbit       = OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, PositionAngle.MEAN, date, getMu(), getFrame());
  315.             final Attitude attitude = getAttitudeProvider().getAttitude(orbit, date, getFrame());

  316.             return new SpacecraftState(orbit, attitude, mass);

  317.         }

  318.         /** {@inheritDoc} */
  319.         public void mapStateToArray(final SpacecraftState state, final double[] y)
  320.             throws OrekitException {

  321.             final Orbit meanOrbit;
  322.             if (!initialIsOsculating) {
  323.                 // the state is considered to be already a mean state
  324.                 meanOrbit = state.getOrbit();
  325.             } else {
  326.                 // the state is considered to be an osculating state
  327.                 final Propagator propagator = createPropagator(state);
  328.                 meanOrbit = new OsculatingToMeanElementsConverter(state, satelliteRevolution, propagator).convert().getOrbit();
  329.             }

  330.             OrbitType.EQUINOCTIAL.mapOrbitToArray(meanOrbit, PositionAngle.MEAN, y);
  331.             y[6] = state.getMass();

  332.         }

  333.         /** Add a force model to the global perturbation model.
  334.          *  <p>
  335.          *  If this method is not called at all,
  336.          *  the integrated orbit will follow a keplerian evolution only.
  337.          *  </p>
  338.          *  @param force perturbing {@link DSSTForceModel force} to add
  339.          *  @see #removeForceModels()
  340.          */
  341.         public void addForceModel(final DSSTForceModel force) {
  342.             forceModels.add(force);
  343.         }

  344.         /** Remove all perturbing force models from the global perturbation model.
  345.          *  <p>
  346.          *  Once all perturbing forces have been removed (and as long as no new force model is added),
  347.          *  the integrated orbit will follow a keplerian evolution only.
  348.          *  </p>
  349.          *  @see #addForceModel(DSSTForceModel)
  350.          */
  351.         public void removeForceModels() {
  352.             forceModels.clear();
  353.         }

  354.         /** Get the force models.
  355.          * @return force models
  356.          */
  357.         public List<DSSTForceModel> getForceModels() {
  358.             return forceModels;
  359.         }

  360.         /** Set the number of satellite revolutions to use for converting osculating to mean elements.
  361.          *  <p>
  362.          *  By default, if the initial orbit is defined as osculating,
  363.          *  it will be averaged over 2 satellite revolutions.
  364.          *  This can be changed by using this method.
  365.          *  </p>
  366.          *  @param satelliteRevolution number of satellite revolutions to use for converting osculating to mean
  367.          *                             elements
  368.          */
  369.         public void setSatelliteRevolution(final int satelliteRevolution) {
  370.             this.satelliteRevolution = satelliteRevolution;
  371.         }

  372.         /** Get the number of satellite revolutions to use for converting osculating to mean elements.
  373.          *  @return number of satellite revolutions to use for converting osculating to mean elements
  374.          */
  375.         public int getSatelliteRevolution() {
  376.             return satelliteRevolution;
  377.         }

  378.         /** Set the osculating parameters flag.
  379.          * @param initialIsOsculating if true, the initial state is provided in osculating elements
  380.          */
  381.         public void setInitialIsOsculating(final boolean initialIsOsculating) {
  382.             this.initialIsOsculating = initialIsOsculating;
  383.         }

  384.         /** Check if the initial state is provided in osculating elements.
  385.          * @return true if initial state is provided in osculating elements
  386.          */
  387.         public boolean initialIsOsculating() {
  388.             return initialIsOsculating;
  389.         }

  390.         /** Create a reference numerical propagator to convert orbit to mean elements.
  391.          *  @param initialState initial state
  392.          *  @return propagator
  393.          *  @throws OrekitException if some numerical force model cannot be built
  394.          */
  395.         private Propagator createPropagator(final SpacecraftState initialState)
  396.             throws OrekitException {
  397.             final Orbit initialOrbit = initialState.getOrbit();
  398.             final double[][] tol = NumericalPropagator.tolerances(1.0, initialOrbit, OrbitType.EQUINOCTIAL);
  399.             final double minStep = 1.;
  400.             final double maxStep = 200.;
  401.             final AdaptiveStepsizeIntegrator integ = new DormandPrince853Integrator(minStep, maxStep, tol[0], tol[1]);
  402.             integ.setInitialStepSize(100.);

  403.             final NumericalPropagator propagator = new NumericalPropagator(integ);
  404.             propagator.setOrbitType(OrbitType.EQUINOCTIAL);
  405.             propagator.setInitialState(initialState);

  406.             // Define the same force model as the DSST
  407.             for (final DSSTForceModel force : forceModels) {
  408.                 if (force instanceof DSSTCentralBody) {
  409.                     // Central body
  410.                     final UnnormalizedSphericalHarmonicsProvider provider = ((DSSTCentralBody) force).getProvider();
  411.                     final ForceModel holmesFeatherstone =
  412.                             new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true),
  413.                                                                   GravityFieldFactory.getNormalizedProvider(provider));
  414.                     propagator.addForceModel(holmesFeatherstone);
  415.                 } else if (force instanceof DSSTThirdBody) {
  416.                     // Third body
  417.                     final CelestialBody body = ((DSSTThirdBody) force).getBody();
  418.                     final ForceModel third   = new ThirdBodyAttraction(body);
  419.                     propagator.addForceModel(third);
  420.                 } else if (force instanceof DSSTAtmosphericDrag) {
  421.                     // Atmospheric drag
  422.                     final Atmosphere atm = ((DSSTAtmosphericDrag) force).getAtmosphere();
  423.                     final double area    = ((DSSTAtmosphericDrag) force).getArea();
  424.                     final double cd      = ((DSSTAtmosphericDrag) force).getCd();
  425.                     final SphericalSpacecraft scr = new SphericalSpacecraft(area, cd, 0., 0.);
  426.                     final ForceModel drag = new DragForce(atm, scr);
  427.                     propagator.addForceModel(drag);
  428.                 } else if (force instanceof DSSTSolarRadiationPressure) {
  429.                     // Solar radiation pressure
  430.                     final double ae   = ((DSSTSolarRadiationPressure) force).getEquatorialRadius();
  431.                     final double area = ((DSSTSolarRadiationPressure) force).getArea();
  432.                     final double cr   = ((DSSTSolarRadiationPressure) force).getCr();
  433.                     // Convert DSST SRP coefficient convention to numerical's one
  434.                     final double kr   = 3.25 - 2.25 * cr;
  435.                     final SphericalSpacecraft scr = new SphericalSpacecraft(area, 0., 0., kr);
  436.                     final ForceModel pressure = new SolarRadiationPressure(CelestialBodyFactory.getSun(), ae, scr);
  437.                     propagator.addForceModel(pressure);
  438.                 }
  439.             }
  440.             return propagator;
  441.         }

  442.         /** Replace the instance with a data transfer object for serialization.
  443.          * @return data transfer object that will be serialized
  444.          * @exception NotSerializableException if one of the force models cannot be serialized
  445.          */
  446.         private Object writeReplace() throws NotSerializableException {

  447.             // Check the force models can be serialized
  448.             final DSSTForceModel[] serializableorceModels = new DSSTForceModel[forceModels.size()];
  449.             for (int i = 0; i < serializableorceModels.length; ++i) {
  450.                 final DSSTForceModel forceModel = forceModels.get(i);
  451.                 if (forceModel instanceof Serializable) {
  452.                     serializableorceModels[i] = forceModel;
  453.                 } else {
  454.                     throw new NotSerializableException(forceModel.getClass().getName());
  455.                 }
  456.             }
  457.             return new DataTransferObject(getReferenceDate(), getMu(), getAttitudeProvider(), getFrame(),
  458.                                           initialIsOsculating, serializableorceModels, satelliteRevolution);
  459.         }

  460.         /** Internal class used only for serialization. */
  461.         private static class DataTransferObject implements Serializable {

  462.             /** Serializable UID. */
  463.             private static final long serialVersionUID = 20130621L;

  464.             /** Reference date. */
  465.             private final AbsoluteDate referenceDate;

  466.             /** Central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>). */
  467.             private final double mu;

  468.             /** Attitude provider. */
  469.             private final AttitudeProvider attitudeProvider;

  470.             /** Inertial frame. */
  471.             private final Frame frame;

  472.             /** Flag specifying whether the initial orbital state is given with osculating elements. */
  473.             private final boolean initialIsOsculating;

  474.             /** Force models to use for short periodic terms computation. */
  475.             private final DSSTForceModel[] forceModels;

  476.             /** Number of satellite revolutions in the averaging interval. */
  477.             private final int satelliteRevolution;

  478.             /** Simple constructor.
  479.              * @param referenceDate reference date
  480.              * @param mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  481.              * @param attitudeProvider attitude provider
  482.              * @param frame inertial frame
  483.              * @param initialIsOsculating if true, initial orbital state is given with osculating elements
  484.              * @param forceModels force models to use for short periodic terms computation
  485.              * @param satelliteRevolution number of satellite revolutions in the averaging interval
  486.              */
  487.             public DataTransferObject(final AbsoluteDate referenceDate, final double mu,
  488.                                       final AttitudeProvider attitudeProvider, final Frame frame,
  489.                                       final boolean initialIsOsculating,
  490.                                       final DSSTForceModel[] forceModels, final int satelliteRevolution) {
  491.                 this.referenceDate       = referenceDate;
  492.                 this.mu                  = mu;
  493.                 this.attitudeProvider    = attitudeProvider;
  494.                 this.frame               = frame;
  495.                 this.initialIsOsculating = initialIsOsculating;
  496.                 this.forceModels         = forceModels;
  497.                 this.satelliteRevolution = satelliteRevolution;
  498.             }

  499.             /** Replace the deserialized data transfer object with a {@link MeanPlusShortPeriodicMapper}.
  500.              * @return replacement {@link MeanPlusShortPeriodicMapper}
  501.              */
  502.             private Object readResolve() {
  503.                 final MeanPlusShortPeriodicMapper mapper =
  504.                         new MeanPlusShortPeriodicMapper(referenceDate, mu, attitudeProvider, frame);
  505.                 for (final DSSTForceModel forceModel : forceModels) {
  506.                     mapper.addForceModel(forceModel);
  507.                 }
  508.                 mapper.setSatelliteRevolution(satelliteRevolution);
  509.                 mapper.setInitialIsOsculating(initialIsOsculating);
  510.                 return mapper;
  511.             }

  512.         }

  513.     }

  514.     /** {@inheritDoc} */
  515.     protected MainStateEquations getMainStateEquations(final AbstractIntegrator integrator) {
  516.         return new Main(integrator);
  517.     }

  518.     /** Internal class for mean parameters integration. */
  519.     private class Main implements MainStateEquations {

  520.         /** Derivatives array. */
  521.         private final double[] yDot;

  522.         /** Simple constructor.
  523.          * @param integrator numerical integrator to use for propagation.
  524.          */
  525.         public Main(final AbstractIntegrator integrator) {
  526.             yDot = new double[7];

  527.             for (final DSSTForceModel forceModel : mapper.getForceModels()) {
  528.                 final EventDetector[] modelDetectors = forceModel.getEventsDetectors();
  529.                 if (modelDetectors != null) {
  530.                     for (final EventDetector detector : modelDetectors) {
  531.                         setUpEventDetector(integrator, detector);
  532.                     }
  533.                 }
  534.             }

  535.         }

  536.         /** {@inheritDoc} */
  537.         public double[] computeDerivatives(final SpacecraftState state) throws OrekitException {

  538.             // compute common auxiliary elements
  539.             final AuxiliaryElements aux = new AuxiliaryElements(state.getOrbit(), I);

  540.             // initialize all perturbing forces
  541.             for (final DSSTForceModel force : mapper.getForceModels()) {
  542.                 force.initializeStep(aux);
  543.             }

  544.             Arrays.fill(yDot, 0.0);

  545.             // compute the contributions of all perturbing forces
  546.             for (final DSSTForceModel forceModel : mapper.getForceModels()) {
  547.                 final double[] daidt = forceModel.getMeanElementRate(state);
  548.                 for (int i = 0; i < daidt.length; i++) {
  549.                     yDot[i] += daidt[i];
  550.                 }
  551.             }

  552.             // finalize derivatives by adding the Kepler contribution
  553.             final EquinoctialOrbit orbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(state.getOrbit());
  554.             orbit.addKeplerContribution(PositionAngle.MEAN, getMu(), yDot);

  555.             return yDot.clone();
  556.         }

  557.     }

  558.     /** Estimate tolerance vectors for an AdaptativeStepsizeIntegrator.
  559.      *  <p>
  560.      *  The errors are estimated from partial derivatives properties of orbits,
  561.      *  starting from a scalar position error specified by the user.
  562.      *  Considering the energy conservation equation V = sqrt(mu (2/r - 1/a)),
  563.      *  we get at constant energy (i.e. on a Keplerian trajectory):
  564.      *
  565.      *  <pre>
  566.      *  V<sup>2</sup> r |dV| = mu |dr|
  567.      *  </pre>
  568.      *
  569.      *  So we deduce a scalar velocity error consistent with the position error. From here, we apply
  570.      *  orbits Jacobians matrices to get consistent errors on orbital parameters.
  571.      *  </p>
  572.      *  <p>
  573.      *  The tolerances are only <em>orders of magnitude</em>, and integrator tolerances are only
  574.      *  local estimates, not global ones. So some care must be taken when using these tolerances.
  575.      *  Setting 1mm as a position error does NOT mean the tolerances will guarantee a 1mm error
  576.      *  position after several orbits integration.
  577.      *  </p>
  578.      *
  579.      * @param dP user specified position error (m)
  580.      * @param orbit reference orbit
  581.      * @return a two rows array, row 0 being the absolute tolerance error
  582.      *                       and row 1 being the relative tolerance error
  583.      * @exception PropagationException if Jacobian is singular
  584.      */
  585.     public static double[][] tolerances(final double dP,
  586.                                         final Orbit orbit)
  587.         throws PropagationException {

  588.         return NumericalPropagator.tolerances(dP, orbit, OrbitType.EQUINOCTIAL);

  589.     }

  590. }