1   /* Copyright 2002-2026 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.relative.maneuver;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.ode.events.Action;
21  import org.orekit.annotation.DefaultDataContext;
22  import org.orekit.propagation.SpacecraftState;
23  import org.orekit.propagation.events.DateDetector;
24  import org.orekit.propagation.events.EventDetector;
25  import org.orekit.propagation.events.handlers.EventHandler;
26  import org.orekit.propagation.relative.RelativeProvider;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.time.TimeScale;
29  import org.orekit.time.TimeScalesFactory;
30  import org.orekit.utils.DataDictionary;
31  import org.orekit.utils.DoubleArrayDictionary;
32  import org.orekit.utils.TimeStampedPVCoordinates;
33  
34  import java.util.Objects;
35  
36  /**
37   * Abstract class for implementing an impulse maneuver for a chaser spacecraft.
38   *
39   * @param <P> type of the relative motion provider
40   * @author Romain Cuvillon
41   * @since 14.0
42   */
43  public abstract class AbstractRelativeManeuver<P extends RelativeProvider> implements RelativeManeuver {
44  
45      /** Trigger event. */
46      private final EventDetector trigger;
47  
48      /** ΔV vector in the target's LOF. */
49      private final Vector3D deltaV;
50  
51      /** True if the propagation is forward in time. */
52      private boolean forward;
53  
54      /** Relative Provider. */
55      private final P relativeProvider;
56  
57      /** Event Handler. */
58      private final EventHandler handler;
59  
60      /**
61       * Creates a new {@link AbstractRelativeManeuver} from an event detector, a ΔV vector, and a
62       * {@link RelativeProvider}.
63       *
64       * @param trigger          Triggering event detector.
65       * @param deltaV           ΔV vector in the local orbital frame of the theory used by the given
66       *                         {@link RelativeProvider}.
67       * @param relativeProvider Relative motion equations provider.
68       */
69      public AbstractRelativeManeuver(final EventDetector trigger, final Vector3D deltaV, final P relativeProvider) {
70          this.trigger          = trigger;
71          this.deltaV           = deltaV;
72          this.relativeProvider = relativeProvider;
73          this.handler          = new Handler();
74      }
75  
76      /** {@inheritDoc} */
77      @Override
78      public void init(final SpacecraftState s0, final AbsoluteDate t) {
79          getDetector().init(s0, t);
80          forward = t.durationFrom(s0.getDate()) >= 0;
81      }
82  
83      /** {@inheritDoc} */
84      @Override
85      public EventDetector getDetector() {
86          return trigger;
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public EventHandler getHandler() {
92          return handler;
93      }
94  
95      /** String representation in UTC.
96       *
97       * @return string representation with date in UTC timescale
98       */
99      @Override
100     @DefaultDataContext
101     public String toString() {
102         return toString(TimeScalesFactory.getUTC());
103     }
104 
105     /**
106      * String representation with timescale.
107      *
108      * @param timeScale timescale to use for date formatting
109      * @return string representation with timescale
110      */
111     public String toString(final TimeScale timeScale) {
112         final String triggerString =
113                         (getDetector() instanceof DateDetector) ?
114                         ((DateDetector) getDetector()).getDate().toString(timeScale) :
115                         getDetector().toString();
116         return "[trigger = " + triggerString + " ; ΔV = " + deltaV + " ; |ΔV| = " + deltaV.getNorm() + "]";
117     }
118 
119     /** Getter for the forward boolean.
120      * @return forward boolean.
121      */
122     public boolean getForward() {
123         return forward;
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public Vector3D getDeltaV() {
129         return deltaV;
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public P getRelativeProvider() {
135         return this.relativeProvider;
136     }
137 
138     /** Local handler.
139      * <p>
140      * Apply the maneuver to the chaser S/C in the additional state.
141      * </p>
142      */
143     private static class Handler implements EventHandler {
144 
145         /** {@inheritDoc} */
146         public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
147             final AbstractRelativeManeuver<?> im = (AbstractRelativeManeuver<?>) detector;
148             final Action underlyingAction = im.getDetector().getHandler().eventOccurred(s, im.getDetector(), increasing);
149             return (underlyingAction == Action.STOP) ? Action.RESET_STATE : Action.CONTINUE;
150         }
151 
152         /** {@inheritDoc} */
153         @Override
154         public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
155             final AbstractRelativeManeuver<?> maneuver = (AbstractRelativeManeuver<?>) detector;
156 
157             // Get velocity increment from maneuver
158             final Vector3D deltaV = maneuver.getDeltaV();
159             final double sign = maneuver.getForward() ? +1 : -1;
160 
161             // Apply increment to velocity of the chaser
162             final double[] chaserPV = oldState.getAdditionalState(maneuver.getRelativeProvider().getName());
163 
164             chaserPV[3] += sign * deltaV.getX();
165             chaserPV[4] += sign * deltaV.getY();
166             chaserPV[5] += sign * deltaV.getZ();
167 
168             // Reset the equations provider with the new chaser state
169             maneuver.getRelativeProvider().setInitialChaserPVTLof(
170                             new TimeStampedPVCoordinates(oldState.getDate(),
171                                                          new Vector3D(chaserPV[0],
172                                                                       chaserPV[1],
173                                                                       chaserPV[2]),
174                                                          new Vector3D(chaserPV[3],
175                                                                       chaserPV[4],
176                                                                       chaserPV[5])));
177 
178             // Reset the TrueAnomaly of the spacecraft to the current true anomaly if YA is used / do nothing if CW.
179             maneuver.resetTrueAnomalyAtManeuver(oldState.getOrbit());
180 
181             // Pack everything in a new state
182             SpacecraftState newState = new SpacecraftState(oldState.getOrbit(), oldState.getAttitude()).withMass(
183                             oldState.getMass());
184 
185             // Add additional equations
186             for (final DataDictionary.Entry entry : oldState.getAdditionalDataValues().getData()) {
187                 if (!Objects.equals(entry.getKey(), maneuver.getRelativeProvider().getName())) {
188                     newState = newState.addAdditionalData(entry.getKey(), entry.getValue());
189                 } else {
190                     // Use modified chaser PV if the additional state is the currently used YA provider
191                     newState = newState.addAdditionalData(maneuver.getRelativeProvider().getName(), chaserPV);
192                 }
193             }
194 
195             // Add additional equations' derivatives
196             for (final DoubleArrayDictionary.Entry entry : oldState.getAdditionalStatesDerivatives().getData()) {
197                 newState = newState.addAdditionalStateDerivative(entry.getKey(), entry.getValue());
198             }
199 
200             return newState;
201         }
202     }
203 }