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  
18  package org.orekit.propagation.relative;
19  
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.orekit.control.heuristics.lambert.LambertSolution;
22  import org.orekit.frames.Frame;
23  import org.orekit.time.AbsoluteDate;
24  import org.orekit.time.TimeUtils;
25  import org.orekit.utils.TimeStampedPVCoordinates;
26  
27  /**
28   * This class stores the solution of a two-impulse rendez-vous. All the contained variables are expressed in the same
29   * frame, accessible through the method getFrame(). Since the PVT of the chaser on both ends of the transfer orbit are
30   * stored in this object in addition to the ΔV vectors, it is possible to reconstruct the orbit of the chaser before and
31   * after the transfer.
32   *
33   * @author Jérôme Tabeaud
34   * @author Romain Cuvillon
35   * @since 14.0
36   */
37  public class TwoImpulseTransfer {
38  
39      /**
40       * PVT of the chaser just after the first maneuver.
41       */
42      private final TimeStampedPVCoordinates pvt1;
43  
44      /**
45       * PVT of the chaser just before the second maneuver.
46       */
47      private final TimeStampedPVCoordinates pvt2;
48  
49      /**
50       * ΔV vector of first maneuver.
51       */
52      private final Vector3D deltaV1;
53  
54      /**
55       * ΔV vector of second maneuver.
56       */
57      private final Vector3D deltaV2;
58  
59      /**
60       * Frame in which the PVT and ΔV are expressed.
61       */
62      private final Frame frame;
63  
64      /**
65       * Creates a new TwoImpulseTransfer object from the given PVT and ΔV vectors.
66       *
67       * @param pvt1    PVT at the start of the transfer
68       * @param pvt2    PVT at the end of the transfer
69       * @param deltaV1 ΔV to enter the transfer orbit
70       * @param deltaV2 ΔV to exit the transfer orbit
71       * @param frame   Frame in which the PVT and ΔV vector are expressed
72       */
73      public TwoImpulseTransfer(final TimeStampedPVCoordinates pvt1,
74                                final TimeStampedPVCoordinates pvt2,
75                                final Vector3D deltaV1,
76                                final Vector3D deltaV2,
77                                final Frame frame) {
78          this.pvt1    = pvt1;
79          this.pvt2    = pvt2;
80          this.deltaV1 = deltaV1;
81          this.deltaV2 = deltaV2;
82          this.frame   = frame;
83      }
84  
85      /**
86       * <p>Creates a new {@link TwoImpulseTransfer} object from a {@link LambertSolution} and the PVs from the departure
87       * and destination orbits.</p>
88       * <p>The ΔV vectors are automatically computed.</p>
89       *
90       * @param solution    Lambert solver's solution
91       * @param v1BeforeMan PV before departure manoeuvre on initial orbit
92       * @param v2AfterMan  PV after arrival manoeuvre on destination orbit
93       */
94      public TwoImpulseTransfer(final LambertSolution solution, final Vector3D v1BeforeMan, final Vector3D v2AfterMan) {
95          final TimeStampedPVCoordinates pvt1AfterMan =
96                          new TimeStampedPVCoordinates(solution.getBoundaryConditions().getInitialDate(),
97                                                       solution.getBoundaryConditions().getInitialPosition(),
98                                                       solution.getBoundaryVelocities().getInitialVelocity());
99          final TimeStampedPVCoordinates pvt2BeforeMan =
100                         new TimeStampedPVCoordinates(solution.getBoundaryConditions().getTerminalDate(),
101                                                      solution.getBoundaryConditions().getTerminalPosition(),
102                                                      solution.getBoundaryVelocities().getTerminalVelocity());
103         this.pvt1    = pvt1AfterMan;
104         this.pvt2    = pvt2BeforeMan;
105         this.deltaV1 = pvt1AfterMan.getVelocity().subtract(v1BeforeMan);
106         this.deltaV2 = pvt2BeforeMan.getVelocity().subtract(v2AfterMan);
107         this.frame   = solution.getBoundaryConditions().getReferenceFrame();
108     }
109 
110     /**
111      * Creates a new {@link TwoImpulseTransfer} object from the given PVT before the first maneuver and after the second
112      * maneuver (on the initial and final orbits), as well as the velocity vectors after the first maneuver and before
113      * the second maneuver (on the transfer orbit).
114      *
115      * @param pvt1BeforeMan PVT before the departure maneuver
116      * @param v1AfterMan    Velocity after the departure maneuver
117      * @param pvt2AfterMan  PVT after the rendez-vous maneuver
118      * @param v2BeforeMan   Velocity before the rendez-vous maneuver
119      * @param frame         Frame in which the PVT and velocity vectors are expressed
120      * @return TwoImpulseTransfer object that corresponds to the inputs
121      */
122     public static TwoImpulseTransfer fromPVTAndVelocities(final TimeStampedPVCoordinates pvt1BeforeMan,
123                                                           final Vector3D v1AfterMan,
124                                                           final TimeStampedPVCoordinates pvt2AfterMan,
125                                                           final Vector3D v2BeforeMan,
126                                                           final Frame frame) {
127         final TimeStampedPVCoordinates pvt1AfterMan =
128                         new TimeStampedPVCoordinates(pvt1BeforeMan.getDate(), pvt1BeforeMan.getPosition(), v1AfterMan);
129         final TimeStampedPVCoordinates pvt2BeforeMan =
130                         new TimeStampedPVCoordinates(pvt2AfterMan.getDate(), pvt2AfterMan.getPosition(), v2BeforeMan);
131         final Vector3D deltaV1 = pvt1AfterMan.getVelocity().subtract(pvt1BeforeMan.getVelocity());
132         final Vector3D deltaV2 = pvt2AfterMan.getVelocity().subtract(pvt2BeforeMan.getVelocity());
133         return new TwoImpulseTransfer(pvt1AfterMan, pvt2BeforeMan, deltaV1, deltaV2, frame);
134     }
135 
136     /**
137      * Get the PVT of the chaser just after the first maneuver.
138      *
139      * @return PVT of the chaser just after the first maneuver
140      */
141     public TimeStampedPVCoordinates getPvt1() {
142         return pvt1;
143     }
144 
145     /**
146      * Get PVT of the chaser just before the second maneuver.
147      *
148      * @return PVT of the chaser just before the second maneuver
149      */
150     public TimeStampedPVCoordinates getPvt2() {
151         return pvt2;
152     }
153 
154     /**
155      * Get the ΔV vector of first maneuver.
156      *
157      * @return The ΔV vector of first maneuver
158      */
159     public Vector3D getDeltaV1() {
160         return deltaV1;
161     }
162 
163     /**
164      * Get the ΔV vector of second maneuver.
165      *
166      * @return ΔV vector of second maneuver
167      */
168     public Vector3D getDeltaV2() {
169         return deltaV2;
170     }
171 
172     /**
173      * Get PVT of the chaser just after the first maneuver.
174      *
175      * @param outputFrame given frame for the first maneuver
176      * @return PVT of the chaser just after the first maneuver
177      */
178     public TimeStampedPVCoordinates getPvt1(final Frame outputFrame) {
179         return frame.getTransformTo(outputFrame, pvt1.getDate()).transformPVCoordinates(pvt1);
180     }
181 
182     /**
183      * Get PVT of the chaser just before the second maneuver.
184      *
185      * @param outputFrame given frame for the 2nd maneuver
186      * @return PVT of the chaser just before the second maneuver
187      */
188     public TimeStampedPVCoordinates getPvt2(final Frame outputFrame) {
189         return frame.getTransformTo(outputFrame, pvt2.getDate()).transformPVCoordinates(pvt2);
190     }
191 
192     /**
193      * Get the ΔV vector of first maneuver in the given frame.
194      *
195      * @param outputFrame given frame for the first maneuver
196      * @return The ΔV vector of first maneuver in the given frame at the given date
197      */
198     public Vector3D getDeltaV1(final Frame outputFrame) {
199         return frame.getTransformTo(outputFrame, pvt1.getDate()).transformVector(deltaV1);
200     }
201 
202     /**
203      * Get the ΔV vector of second maneuver in the given frame at the given date.
204      *
205      * @param outputFrame given frame for the 2nd maneuver
206      * @return The ΔV vector of second maneuver in the given frame at the given date
207      */
208     public Vector3D getDeltaV2(final Frame outputFrame) {
209         return frame.getTransformTo(outputFrame, pvt2.getDate()).transformVector(deltaV2);
210     }
211 
212     /**
213      * Computes the total ΔV of the transfer.
214      *
215      * @return ||ΔV_total|| = ||ΔV1|| + ||ΔV2||
216      */
217     public double getTotalDeltaV() {
218         return deltaV1.getNorm() + deltaV2.getNorm();
219     }
220 
221     /**
222      * Get the duration of the transfer between the two impulses.
223      *
224      * @return The duration of the transfer between the two impulses
225      */
226     public double getDuration() {
227         return pvt2.durationFrom(pvt1);
228     }
229 
230     /**
231      * Returns the departure date.
232      *
233      * @return the departure date
234      */
235     public AbsoluteDate getDepartureDate() {
236         return pvt1.getDate();
237     }
238 
239     /**
240      * Returns the arrival date.
241      *
242      * @return the arrival date
243      */
244     public AbsoluteDate getArrivalDate() {
245         return pvt2.getDate();
246     }
247 
248     /**
249      * Returns the frame in which all elements contained in the object are expressed.
250      *
251      * @return The frame of the elements of the object
252      */
253     public Frame getFrame() {
254         return frame;
255     }
256 
257     /**
258      * Returns the PVT of the chaser just before the injection maneuver into the transfer orbit.
259      * @return The PVT before the injection maneuver into the transfer orbit
260      */
261     public TimeStampedPVCoordinates getPvt1BeforeMan() {
262         return new TimeStampedPVCoordinates(pvt1.getDate(), pvt1.getPosition(), pvt1.getVelocity().subtract(deltaV1));
263     }
264 
265     /**
266      * Returns the PVT of the chaser just after the velocity-synchronization maneuver into the target orbit.
267      * @return The PVT after the velocity-synchronization maneuver into the target orbit
268      */
269     public TimeStampedPVCoordinates getPvt2AfterMan() {
270         return new TimeStampedPVCoordinates(pvt2.getDate(), pvt2.getPosition(), pvt2.getVelocity().add(deltaV2));
271     }
272 
273     /**
274      * Converts all the PVT and ΔV vectors to the desired frame.
275      *
276      * @param outputFrame Desired output frame
277      * @return TwoImpulseTransfer object expressed in the desired frame
278      */
279     public TwoImpulseTransfer expressInFrame(final Frame outputFrame) {
280         return new TwoImpulseTransfer(getPvt1(outputFrame),
281                                       getPvt2(outputFrame),
282                                       getDeltaV1(outputFrame),
283                                       getDeltaV2(outputFrame),
284                                       outputFrame);
285     }
286 
287     /**
288      * Writes a string summarizing the transfer characteristics (PVT and ΔV vectors).
289      *
290      * @return The string summary
291      */
292     public String toString() {
293         return "Two impulse transfer\n" +
294                "\n    Frame:                       " + getFrame().getName() +
295                "\n    PVT after first manoeuvre:   " + getPvt1() +
296                "\n    PVT before second manoeuvre: " + getPvt2() +
297                "\n    ΔV of first manoeuvre:       " + getDeltaV1() + " ; ‖ΔV₁‖ = " + getDeltaV1().getNorm() +
298                "\n    ΔV of second manoeuvre:      " + getDeltaV2() + " ; ‖ΔV₂‖ = " + getDeltaV2().getNorm() +
299                "\n    Total ΔV:                    " + getTotalDeltaV() +
300                "\n    Duration:                    " + TimeUtils.secondsToDHMS(getDuration()) + "\n";
301     }
302 }