KeplerianPropagator.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.propagation.analytical;

  18. import java.io.NotSerializableException;
  19. import java.io.Serializable;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.SortedSet;

  25. import org.orekit.attitudes.Attitude;
  26. import org.orekit.attitudes.AttitudeProvider;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.orbits.Orbit;
  30. import org.orekit.orbits.OrbitType;
  31. import org.orekit.orbits.PositionAngle;
  32. import org.orekit.propagation.AdditionalStateProvider;
  33. import org.orekit.propagation.SpacecraftState;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.utils.TimeSpanMap;

  36. /** Simple Keplerian orbit propagator.
  37.  * @see Orbit
  38.  * @author Guylaine Prat
  39.  */
  40. public class KeplerianPropagator extends AbstractAnalyticalPropagator implements Serializable {

  41.     /** Serializable UID. */
  42.     private static final long serialVersionUID = 20151117L;

  43.     /** Initial state. */
  44.     private SpacecraftState initialState;

  45.     /** All states. */
  46.     private transient TimeSpanMap<SpacecraftState> states;

  47.     /** Build a propagator from orbit only.
  48.      * <p>The central attraction coefficient μ is set to the same value used
  49.      * for the initial orbit definition. Mass and attitude provider are set to
  50.      * unspecified non-null arbitrary values.</p>
  51.      * @param initialOrbit initial orbit
  52.      * @exception OrekitException if initial attitude cannot be computed
  53.      */
  54.     public KeplerianPropagator(final Orbit initialOrbit)
  55.         throws OrekitException {
  56.         this(initialOrbit, DEFAULT_LAW, initialOrbit.getMu(), DEFAULT_MASS);
  57.     }

  58.     /** Build a propagator from orbit and central attraction coefficient μ.
  59.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  60.      * @param initialOrbit initial orbit
  61.      * @param mu central attraction coefficient (m³/s²)
  62.      * @exception OrekitException if initial attitude cannot be computed
  63.      */
  64.     public KeplerianPropagator(final Orbit initialOrbit, final double mu)
  65.         throws OrekitException {
  66.         this(initialOrbit, DEFAULT_LAW, mu, DEFAULT_MASS);
  67.     }

  68.     /** Build a propagator from orbit and attitude provider.
  69.      * <p>The central attraction coefficient μ is set to the same value
  70.      * used for the initial orbit definition. Mass is set to an unspecified
  71.      * non-null arbitrary value.</p>
  72.      * @param initialOrbit initial orbit
  73.      * @param attitudeProv  attitude provider
  74.      * @exception OrekitException if initial attitude cannot be computed
  75.      */
  76.     public KeplerianPropagator(final Orbit initialOrbit,
  77.                                final AttitudeProvider attitudeProv)
  78.         throws OrekitException {
  79.         this(initialOrbit, attitudeProv, initialOrbit.getMu(), DEFAULT_MASS);
  80.     }

  81.     /** Build a propagator from orbit, attitude provider and central attraction
  82.      * coefficient μ.
  83.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  84.      * @param initialOrbit initial orbit
  85.      * @param attitudeProv attitude provider
  86.      * @param mu central attraction coefficient (m³/s²)
  87.      * @exception OrekitException if initial attitude cannot be computed
  88.      */
  89.     public KeplerianPropagator(final Orbit initialOrbit,
  90.                                final AttitudeProvider attitudeProv,
  91.                                final double mu)
  92.         throws OrekitException {
  93.         this(initialOrbit, attitudeProv, mu, DEFAULT_MASS);
  94.     }

  95.     /** Build propagator from orbit, attitude provider, central attraction
  96.      * coefficient μ and mass.
  97.      * @param initialOrbit initial orbit
  98.      * @param attitudeProv attitude provider
  99.      * @param mu central attraction coefficient (m³/s²)
  100.      * @param mass spacecraft mass (kg)
  101.      * @exception OrekitException if initial attitude cannot be computed
  102.      */
  103.     public KeplerianPropagator(final Orbit initialOrbit, final AttitudeProvider attitudeProv,
  104.                                final double mu, final double mass)
  105.         throws OrekitException {

  106.         super(attitudeProv);

  107.         // ensure the orbit use the specified mu and has no non-Keplerian derivatives
  108.         initialState = fixState(initialOrbit,
  109.                                 getAttitudeProvider().getAttitude(initialOrbit,
  110.                                                                   initialOrbit.getDate(),
  111.                                                                   initialOrbit.getFrame()),
  112.                                 mass, mu, Collections.emptyMap());
  113.         states = new TimeSpanMap<SpacecraftState>(initialState);
  114.         super.resetInitialState(initialState);

  115.     }

  116.     /** Fix state to use a specified mu and remove derivatives.
  117.      * <p>
  118.      * This ensures the propagation model (which is based on calling
  119.      * {@link Orbit#shiftedBy(double)}) is Keplerian only and uses a specified mu.
  120.      * </p>
  121.      * @param orbit orbit to fix
  122.      * @param attitude current attitude
  123.      * @param mass current mass
  124.      * @param mu gravity coefficient to use
  125.      * @param additionalStates additional states
  126.      * @return fixed orbit
  127.      */
  128.     private SpacecraftState fixState(final Orbit orbit, final Attitude attitude, final double mass,
  129.                                      final double mu, final Map<String, double[]> additionalStates) {
  130.         final OrbitType type = orbit.getType();
  131.         final double[] stateVector = new double[6];
  132.         type.mapOrbitToArray(orbit, PositionAngle.TRUE, stateVector, null);
  133.         final Orbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngle.TRUE,
  134.                                                       orbit.getDate(), mu, orbit.getFrame());
  135.         SpacecraftState fixedState = new SpacecraftState(fixedOrbit, attitude, mass);
  136.         for (final Map.Entry<String, double[]> entry : additionalStates.entrySet()) {
  137.             fixedState = fixedState.addAdditionalState(entry.getKey(), entry.getValue());
  138.         }
  139.         return fixedState;
  140.     }

  141.     /** {@inheritDoc} */
  142.     public void resetInitialState(final SpacecraftState state)
  143.         throws OrekitException {

  144.         // ensure the orbit use the specified mu and has no non-Keplerian derivatives
  145.         final double mu = initialState == null ? state.getMu() : initialState.getMu();
  146.         final SpacecraftState fixedState = fixState(state.getOrbit(),
  147.                                                     state.getAttitude(),
  148.                                                     state.getMass(),
  149.                                                     mu,
  150.                                                     state.getAdditionalStates());

  151.         initialState = fixedState;
  152.         states       = new TimeSpanMap<SpacecraftState>(initialState);
  153.         super.resetInitialState(fixedState);

  154.     }

  155.     /** {@inheritDoc} */
  156.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward)
  157.         throws OrekitException {
  158.         if (forward) {
  159.             states.addValidAfter(state, state.getDate());
  160.         } else {
  161.             states.addValidBefore(state, state.getDate());
  162.         }
  163.     }

  164.     /** {@inheritDoc} */
  165.     protected Orbit propagateOrbit(final AbsoluteDate date)
  166.         throws OrekitException {

  167.         // propagate orbit
  168.         Orbit orbit = states.get(date).getOrbit();
  169.         do {
  170.             // we use a loop here to compensate for very small date shifts error
  171.             // that occur with long propagation time
  172.             orbit = orbit.shiftedBy(date.durationFrom(orbit.getDate()));
  173.         } while (!date.equals(orbit.getDate()));

  174.         return orbit;

  175.     }

  176.     /** {@inheritDoc}*/
  177.     protected double getMass(final AbsoluteDate date) {
  178.         return states.get(date).getMass();
  179.     }

  180.     /** Replace the instance with a data transfer object for serialization.
  181.      * @return data transfer object that will be serialized
  182.      * @exception NotSerializableException if an additional state provider is not serializable
  183.      */
  184.     private Object writeReplace() throws NotSerializableException {
  185.         try {

  186.             // managed states providers
  187.             final List<AdditionalStateProvider> serializableProviders = new ArrayList<AdditionalStateProvider>();
  188.             for (final AdditionalStateProvider provider : getAdditionalStateProviders()) {
  189.                 if (provider instanceof Serializable) {
  190.                     serializableProviders.add(provider);
  191.                 } else {
  192.                     throw new NotSerializableException(provider.getClass().getName());
  193.                 }
  194.             }

  195.             // states transitions
  196.             final AbsoluteDate[]    transitionDates;
  197.             final SpacecraftState[] allStates;
  198.             final SortedSet<TimeSpanMap.Transition<SpacecraftState>> transitions = states.getTransitions();
  199.             if (transitions.size() == 1  && transitions.first().getBefore() == transitions.first().getAfter()) {
  200.                 // the single entry is a dummy one, without a real transition
  201.                 // we ignore it completely
  202.                 transitionDates = null;
  203.                 allStates       = null;
  204.             } else {
  205.                 transitionDates = new AbsoluteDate[transitions.size()];
  206.                 allStates       = new SpacecraftState[transitions.size() + 1];
  207.                 int i = 0;
  208.                 for (final TimeSpanMap.Transition<SpacecraftState> transition : transitions) {
  209.                     if (i == 0) {
  210.                         // state before the first transition
  211.                         allStates[i] = transition.getBefore();
  212.                     }
  213.                     transitionDates[i] = transition.getDate();
  214.                     allStates[++i]     = transition.getAfter();
  215.                 }
  216.             }

  217.             return new DataTransferObject(getInitialState().getOrbit(), getAttitudeProvider(),
  218.                                           getInitialState().getMu(), getInitialState().getMass(),
  219.                                           transitionDates, allStates,
  220.                                           serializableProviders.toArray(new AdditionalStateProvider[serializableProviders.size()]));
  221.         } catch (OrekitException orekitException) {
  222.             // this should never happen
  223.             throw new OrekitInternalError(null);
  224.         }

  225.     }

  226.     /** Internal class used only for serialization. */
  227.     private static class DataTransferObject implements Serializable {

  228.         /** Serializable UID. */
  229.         private static final long serialVersionUID = 20151202L;

  230.         /** Initial orbit. */
  231.         private final Orbit orbit;

  232.         /** Attitude provider. */
  233.         private final AttitudeProvider attitudeProvider;

  234.         /** Central attraction coefficient (m³/s²). */
  235.         private final double mu;

  236.         /** Spacecraft mass (kg). */
  237.         private final double mass;

  238.         /** Transition dates (may be null). */
  239.         private final AbsoluteDate[] transitionDates;

  240.         /** States before and after transitions (may be null). */
  241.         private final SpacecraftState[] allStates;

  242.         /** Providers for additional states. */
  243.         private final AdditionalStateProvider[] providers;

  244.         /** Simple constructor.
  245.          * @param orbit initial orbit
  246.          * @param attitudeProvider attitude provider
  247.          * @param mu central attraction coefficient (m³/s²)
  248.          * @param mass initial spacecraft mass (kg)
  249.          * @param transitionDates transition dates (may be null)
  250.          * @param allStates states before and after transitions (may be null)
  251.          * @param providers providers for additional states
  252.          */
  253.         DataTransferObject(final Orbit orbit,
  254.                            final AttitudeProvider attitudeProvider,
  255.                            final double mu, final double mass,
  256.                            final AbsoluteDate[] transitionDates,
  257.                            final SpacecraftState[] allStates,
  258.                            final AdditionalStateProvider[] providers) {
  259.             this.orbit            = orbit;
  260.             this.attitudeProvider = attitudeProvider;
  261.             this.mu               = mu;
  262.             this.mass             = mass;
  263.             this.transitionDates  = transitionDates;
  264.             this.allStates        = allStates;
  265.             this.providers        = providers;
  266.         }

  267.         /** Replace the deserialized data transfer object with a {@link KeplerianPropagator}.
  268.          * @return replacement {@link KeplerianPropagator}
  269.          */
  270.         private Object readResolve() {
  271.             try {

  272.                 final KeplerianPropagator propagator =
  273.                                 new KeplerianPropagator(orbit, attitudeProvider, mu, mass);
  274.                 for (final AdditionalStateProvider provider : providers) {
  275.                     propagator.addAdditionalStateProvider(provider);
  276.                 }

  277.                 if (transitionDates != null) {
  278.                     // override the state transitions
  279.                     propagator.states = new TimeSpanMap<SpacecraftState>(allStates[0]);
  280.                     for (int i = 0; i < transitionDates.length; ++i) {
  281.                         propagator.states.addValidAfter(allStates[i + 1], transitionDates[i]);
  282.                     }
  283.                 }

  284.                 return propagator;

  285.             } catch (OrekitException oe) {
  286.                 throw new OrekitInternalError(oe);
  287.             }
  288.         }

  289.     }

  290. }