1   /* Copyright 2002-2025 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.propagation.analytical;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitMessages;
25  import org.orekit.orbits.Orbit;
26  import org.orekit.propagation.Propagator;
27  import org.orekit.propagation.SpacecraftState;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.utils.DoubleArrayDictionary;
30  import org.orekit.utils.DataDictionary;
31  
32  /** Orbit propagator that adapts an underlying propagator, adding {@link
33   * DifferentialEffect differential effects}.
34   * <p>
35   * This propagator is used when a reference propagator does not handle
36   * some effects that we need. A typical example would be an ephemeris
37   * that was computed for a reference orbit, and we want to compute a
38   * station-keeping maneuver on top of this ephemeris, changing its
39   * final state. The principal is to add one or more {@link
40   * org.orekit.forces.maneuvers.SmallManeuverAnalyticalModel small maneuvers
41   * analytical models} to it and use it as a new propagator, which takes the
42   * maneuvers into account.
43   * </p>
44   * <p>
45   * From a space flight dynamics point of view, this is a differential
46   * correction approach. From a computer science point of view, this is
47   * a use of the decorator design pattern.
48   * </p>
49   * @see Propagator
50   * @see org.orekit.forces.maneuvers.SmallManeuverAnalyticalModel
51   * @author Luc Maisonobe
52   */
53  public class AdapterPropagator extends AbstractAnalyticalPropagator {
54  
55      /** Interface for orbit differential effects. */
56      public interface DifferentialEffect {
57  
58          /** Apply the effect to a {@link SpacecraftState spacecraft state}.
59           * <p>
60           * Applying the effect may be a no-op in some cases. A typical example
61           * is maneuvers, for which the state is changed only for time <em>after</em>
62           * the maneuver occurrence.
63           * </p>
64           * @param original original state <em>without</em> the effect
65           * @return updated state at the same date, taking the effect
66           * into account if meaningful
67           */
68          SpacecraftState apply(SpacecraftState original);
69  
70      }
71  
72      /** Underlying reference propagator. */
73      private Propagator reference;
74  
75      /** Effects to add. */
76      private List<DifferentialEffect> effects;
77  
78      /** Build a propagator from an underlying reference propagator.
79       * <p>The reference propagator can be almost anything, numerical,
80       * analytical, and even an ephemeris. It may already take some maneuvers
81       * into account.</p>
82       * @param reference reference propagator
83       */
84      public AdapterPropagator(final Propagator reference) {
85          super(reference.getAttitudeProvider());
86          this.reference = reference;
87          this.effects = new ArrayList<>();
88          // Sets initial state
89          super.resetInitialState(getInitialState());
90      }
91  
92      /** Add a differential effect.
93       * @param effect differential effect
94       */
95      public void addEffect(final DifferentialEffect effect) {
96          effects.add(effect);
97      }
98  
99      /** Get the reference propagator.
100      * @return reference propagator
101      */
102     public Propagator getPropagator() {
103         return reference;
104     }
105 
106     /** Get the differential effects.
107      * @return differential effects models, as an unmodifiable list
108      */
109     public List<DifferentialEffect> getEffects() {
110         return Collections.unmodifiableList(effects);
111     }
112 
113     /** {@inheritDoc} */
114     public SpacecraftState getInitialState() {
115         return reference.getInitialState();
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public void resetInitialState(final SpacecraftState state) {
121         reference.resetInitialState(state);
122     }
123 
124     /** {@inheritDoc} */
125     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
126         if (reference instanceof AbstractAnalyticalPropagator) {
127             ((AbstractAnalyticalPropagator) reference).resetIntermediateState(state, forward);
128         } else {
129             throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
130         }
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public SpacecraftState basicPropagate(final AbsoluteDate date) {
136 
137         // compute reference state
138         SpacecraftState state = reference.propagate(date);
139         final DataDictionary additionalBefore    = state.getAdditionalDataValues();
140         final DoubleArrayDictionary additionalDotBefore = state.getAdditionalStatesDerivatives();
141 
142         // add all the effects
143         for (final DifferentialEffect effect : effects) {
144             state = effect.apply(state);
145         }
146 
147         // forward additional states and derivatives from the reference propagator
148         for (final DataDictionary.Entry entry : additionalBefore.getData()) {
149             if (!state.hasAdditionalData(entry.getKey())) {
150                 state = state.addAdditionalData(entry.getKey(), entry.getValue());
151             }
152         }
153         for (final DoubleArrayDictionary.Entry entry : additionalDotBefore.getData()) {
154             if (!state.hasAdditionalData(entry.getKey())) {
155                 state = state.addAdditionalStateDerivative(entry.getKey(), entry.getValue());
156             }
157         }
158 
159         return state;
160 
161     }
162 
163     /** {@inheritDoc} */
164     public Orbit propagateOrbit(final AbsoluteDate date) {
165         return basicPropagate(date).getOrbit();
166     }
167 
168     /** {@inheritDoc}*/
169     protected double getMass(final AbsoluteDate date) {
170         return basicPropagate(date).getMass();
171     }
172 
173 }