1   /* Copyright 2002-2021 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.bodies;
18  
19  import org.hipparchus.analysis.UnivariateFunction;
20  import org.hipparchus.analysis.solvers.AllowedSolution;
21  import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
22  import org.hipparchus.analysis.solvers.UnivariateSolverUtils;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.util.FastMath;
25  import org.orekit.frames.CR3BPRotatingFrame;
26  import org.orekit.frames.Frame;
27  import org.orekit.frames.Transform;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.utils.AbsolutePVCoordinates;
30  import org.orekit.utils.LagrangianPoints;
31  import org.orekit.utils.PVCoordinates;
32  import org.orekit.utils.TimeStampedPVCoordinates;
33  
34  /**
35   * Class creating, from two different celestial bodies, the corresponding system
36   * with respect to the Circular Restricted Three Body problem hypotheses.
37   * @see "Dynamical systems, the three-body problem, and space mission design, Koon, Lo, Marsden, Ross"
38   * @author Vincent Mouraux
39   * @author William Desprats
40   * @since 10.2
41   */
42  public class CR3BPSystem {
43  
44      /** Relative accuracy on position for solver. */
45      private static final double RELATIVE_ACCURACY = 1e-14;
46  
47      /** Absolute accuracy on position for solver (1mm). */
48      private static final double ABSOLUTE_ACCURACY = 1e-3;
49  
50      /** Function value accuracy for solver (set to 0 so we rely only on position for convergence). */
51      private static final double FUNCTION_ACCURACY = 0;
52  
53      /** Maximal order for solver. */
54      private static final int MAX_ORDER = 5;
55  
56      /** Maximal number of evaluations for solver. */
57      private static final int MAX_EVALUATIONS = 10000;
58  
59      /** Mass ratio. */
60      private final double mu;
61  
62      /** Distance between the two primaries, meters. */
63      private final double dDim;
64  
65      /** Orbital Velocity of m1, m/s. */
66      private final double vDim;
67  
68      /** Orbital Period of m1 and m2, seconds. */
69      private final double tDim;
70  
71      /** CR3BP System name. */
72      private final String name;
73  
74      /** Rotating Frame for the system. */
75      private final Frame rotatingFrame;
76  
77      /** Primary body. */
78      private final CelestialBody primaryBody;
79  
80      /** Secondary body. */
81      private final CelestialBody secondaryBody;
82  
83      /** L1 Point position in the rotating frame. */
84      private Vector3D l1Position;
85  
86      /** L2 Point position in the rotating frame. */
87      private Vector3D l2Position;
88  
89      /** L3 Point position in the rotating frame. */
90      private Vector3D l3Position;
91  
92      /** L4 Point position in the rotating frame. */
93      private Vector3D l4Position;
94  
95      /** L5 Point position in the rotating frame. */
96      private Vector3D l5Position;
97  
98      /** Distance between a L1 and the secondaryBody. */
99      private final double gamma1;
100 
101     /** Distance between a L2 and the secondaryBody. */
102     private final double gamma2;
103 
104     /** Distance between a L3 and the primaryBody. */
105     private final double gamma3;
106 
107     /**
108      * Simple constructor.
109      * <p>
110      * Standard constructor to use to define a CR3BP System. Mass ratio is
111      * calculated from JPL data.
112      * </p>
113      * @param primaryBody Primary Body in the CR3BP System
114      * @param secondaryBody Secondary Body in the CR3BP System
115      * @param a Semi-Major Axis of the secondary body
116      */
117     public CR3BPSystem(final CelestialBody primaryBody, final CelestialBody secondaryBody, final double a) {
118         this(primaryBody, secondaryBody, a, secondaryBody.getGM() / (secondaryBody.getGM() + primaryBody.getGM()));
119     }
120 
121     /**
122      * Simple constructor.
123      * <p>
124      * This constructor can be used to define a CR3BP System if the user wants
125      * to use a specified mass ratio.
126      * </p>
127      * @param primaryBody Primary Body in the CR3BP System
128      * @param secondaryBody Secondary Body in the CR3BP System
129      * @param a Semi-Major Axis of the secondary body
130      * @param mu Mass ratio of the CR3BPSystem
131      */
132     public CR3BPSystem(final CelestialBody primaryBody, final CelestialBody secondaryBody, final double a, final double mu) {
133         this.primaryBody = primaryBody;
134         this.secondaryBody = secondaryBody;
135 
136         this.name = primaryBody.getName() + "_" + secondaryBody.getName();
137 
138         final double mu1 = primaryBody.getGM();
139 
140         this.mu = mu;
141         this.dDim = a;
142         this.vDim = FastMath.sqrt(mu1 / dDim);
143         this.tDim = 2 * FastMath.PI * dDim / vDim;
144         this.rotatingFrame = new CR3BPRotatingFrame(mu, primaryBody, secondaryBody);
145 
146         computeLagrangianPointsPosition();
147 
148         // Calculation of collinear points gamma
149 
150         // L1 Gamma
151         final double x1 = l1Position.getX();
152         final double dCP1 = 1 - mu;
153         this.gamma1 = dCP1 - x1;
154 
155         // L2 Gamma
156         final double x2 = l2Position.getX();
157         final double dCP2 = 1 - mu;
158         this.gamma2 = x2 - dCP2;
159 
160         // L3 Gamma
161         final double x3 = l3Position.getX();
162         final double dCP3 = -mu;
163         this.gamma3 = dCP3 - x3;
164 
165     }
166 
167     /** Calculation of Lagrangian Points position using CR3BP equations.
168      */
169     private void computeLagrangianPointsPosition() {
170         // Calculation of Lagrangian Points position using CR3BP equations
171 
172         // L1
173         final BracketingNthOrderBrentSolver solver =
174                         new BracketingNthOrderBrentSolver(RELATIVE_ACCURACY,
175                                                           ABSOLUTE_ACCURACY,
176                                                           FUNCTION_ACCURACY, MAX_ORDER);
177 
178         final double baseR1 = 1 - FastMath.cbrt(mu / 3);
179         final UnivariateFunction l1Equation = x -> {
180             final double leq1 =
181                             x * (x + mu) * (x + mu) * (x + mu - 1) * (x + mu - 1);
182             final double leq2 = (1 - mu) * (x + mu - 1) * (x + mu - 1);
183             final double leq3 = mu * (x + mu) * (x + mu);
184             return leq1 - leq2 + leq3;
185         };
186         final double[] searchInterval1 =
187                         UnivariateSolverUtils.bracket(l1Equation, baseR1, -mu,
188                                                       1 - mu, 1E-6, 1,
189                                                       MAX_EVALUATIONS);
190 
191         final double r1 =
192                         solver.solve(MAX_EVALUATIONS, l1Equation, searchInterval1[0],
193                                      searchInterval1[1], AllowedSolution.ANY_SIDE);
194 
195         this.l1Position = new Vector3D(r1, 0.0, 0.0);
196 
197         // L2
198         final double baseR2 = 1 + FastMath.cbrt(mu / 3);
199         final UnivariateFunction l2Equation = x -> {
200             final double leq21 =
201                             x * (x + mu) * (x + mu) * (x + mu - 1) * (x + mu - 1);
202             final double leq22 = (1 - mu) * (x + mu - 1) * (x + mu - 1);
203             final double leq23 = mu * (x + mu) * (x + mu);
204             return leq21 - leq22 - leq23;
205         };
206         final double[] searchInterval2 =
207                         UnivariateSolverUtils.bracket(l2Equation, baseR2, 1 - mu, 2, 1E-6,
208                                                       1, MAX_EVALUATIONS);
209 
210         final double r2 =
211                         solver.solve(MAX_EVALUATIONS, l2Equation, searchInterval2[0],
212                                      searchInterval2[1], AllowedSolution.ANY_SIDE);
213 
214         this.l2Position = new Vector3D(r2, 0.0, 0.0);
215 
216         // L3
217         final double baseR3 = -(1 + 5 * mu / 12);
218         final UnivariateFunction l3Equation = x -> {
219             final double leq31 =
220                             x * (x + mu) * (x + mu) * (x + mu - 1) * (x + mu - 1);
221             final double leq32 = (1 - mu) * (x + mu - 1) * (x + mu - 1);
222             final double leq33 = mu * (x + mu) * (x + mu);
223             return leq31 + leq32 + leq33;
224         };
225         final double[] searchInterval3 =
226                         UnivariateSolverUtils.bracket(l3Equation, baseR3, -2, -mu, 1E-6, 1,
227                                                       MAX_EVALUATIONS);
228 
229         final double r3 =
230                         solver.solve(MAX_EVALUATIONS, l3Equation, searchInterval3[0],
231                                      searchInterval3[1], AllowedSolution.ANY_SIDE);
232 
233         this.l3Position = new Vector3D(r3, 0.0, 0.0);
234 
235         // L4
236         this.l4Position = new Vector3D(0.5 - mu, FastMath.sqrt(3) / 2, 0);
237 
238         // L5
239         this.l5Position = new Vector3D(0.5 - mu, -FastMath.sqrt(3) / 2, 0);
240     }
241 
242     /** Get the CR3BP mass ratio of the system mu2/(mu1+mu2).
243      * @return CR3BP mass ratio of the system mu2/(mu1+mu2)
244      */
245     public double getMassRatio() {
246         return mu;
247     }
248 
249     /** Get the CR3BP distance between the two bodies.
250      * @return CR3BP distance between the two bodies(m)
251      */
252     public double getDdim() {
253         return dDim;
254     }
255 
256     /** Get the CR3BP orbital velocity of m2.
257      * @return CR3BP orbital velocity of m2(m/s)
258      */
259     public double getVdim() {
260         return vDim;
261     }
262 
263     /** Get the CR3BP orbital period of m2 around m1.
264      * @return CR3BP orbital period of m2 around m1(s)
265      */
266     public double getTdim() {
267         return tDim;
268     }
269 
270     /** Get the name of the CR3BP system.
271      * @return name of the CR3BP system
272      */
273     public String getName() {
274         return name;
275     }
276 
277     /** Get the primary CelestialBody.
278      * @return primary CelestialBody
279      */
280     public CelestialBody getPrimary() {
281         return primaryBody;
282     }
283 
284     /** Get the secondary CelestialBody.
285      * @return secondary CelestialBody
286      */
287     public CelestialBody getSecondary() {
288         return secondaryBody;
289     }
290 
291     /** Get the CR3BP Rotating Frame.
292      * @return CR3BP Rotating Frame
293      */
294     public Frame getRotatingFrame() {
295         return rotatingFrame;
296     }
297 
298     /** Get the position of the Lagrangian point in the CR3BP Rotating frame.
299      * @param lagrangianPoint Lagrangian Point to consider
300      * @return position of the Lagrangian point in the CR3BP Rotating frame (-)
301      */
302     public Vector3D getLPosition(final LagrangianPoints lagrangianPoint) {
303 
304         final Vector3D lPosition;
305 
306         switch (lagrangianPoint) {
307 
308             case L1:
309                 lPosition = l1Position;
310                 break;
311 
312             case L3:
313                 lPosition = l3Position;
314                 break;
315 
316             case L4:
317                 lPosition = l4Position;
318                 break;
319 
320             case L5:
321                 lPosition = l5Position;
322                 break;
323 
324             default:
325                 lPosition = l2Position;
326                 break;
327 
328         }
329         return lPosition;
330     }
331 
332     /**
333      * Get the position of the Lagrangian point in the CR3BP Rotating frame.
334      * @param lagrangianPoint Lagrangian Point to consider
335      * @return Distance between a Lagrangian Point and its closest primary.
336      */
337     public double getGamma(final LagrangianPoints lagrangianPoint) {
338 
339         final double gamma;
340 
341         switch (lagrangianPoint) {
342 
343             case L1:
344                 gamma = gamma1;
345                 break;
346 
347             case L2:
348                 gamma = gamma2;
349                 break;
350 
351             case L3:
352                 gamma = gamma3;
353                 break;
354 
355             default:
356                 gamma = 0;
357         }
358         return gamma;
359     }
360 
361     /** Get the PVCoordinates from normalized units to standard units in an output frame.
362      * @param pv0 Normalized PVCoordinates in the rotating frame
363      * @param date Date of the transform
364      * @param outputFrame Frame in which the output PVCoordinates will be
365      * @return PVCoordinates in the output frame [m,m/s]
366      */
367     private PVCoordinates getRealPV(final PVCoordinates pv0, final AbsoluteDate date, final Frame outputFrame) {
368         // 1.   Dimensionalize  the  primary-centered  rotating  state  using  the  instantaneously
369         //      defined characteristic quantities
370         // 2.   Apply the transformation to primary inertial frame
371         // 3.   Apply the transformation to output frame
372 
373         final Frame primaryInertialFrame = primaryBody.getInertiallyOrientedFrame();
374         final TimeStampedPVCoordinates pv21 = secondaryBody.getPVCoordinates(date, primaryInertialFrame);
375 
376         // Distance and Velocity to dimensionalize the state vector
377         final double dist12 = pv21.getPosition().getNorm();
378         final double vCircular  = FastMath.sqrt(primaryBody.getGM() / dist12);
379 
380         // Dimensionalized state vector centered on primary body
381         final PVCoordinates pvDim = new PVCoordinates(pv0.getPosition().scalarMultiply(dist12),
382                                                       pv0.getVelocity().scalarMultiply(vCircular));
383 
384         // Transformation between rotating frame and the primary inertial
385         final Transform rotatingToPrimaryInertial = rotatingFrame.getTransformTo(primaryInertialFrame, date);
386 
387         // State vector in the primary inertial frame
388         final PVCoordinates pv2 = rotatingToPrimaryInertial.transformPVCoordinates(pvDim);
389 
390 
391         // Transformation between primary inertial frame and the output frame
392         final Transform primaryInertialToOutputFrame = primaryInertialFrame.getTransformTo(outputFrame, date);
393 
394         return primaryInertialToOutputFrame.transformPVCoordinates(pv2);
395     }
396 
397     /** Get the AbsolutePVCoordinates from normalized units to standard units in an output frame.
398      * This method ensure the constituency of the date of returned AbsolutePVCoordinate, especially
399      * when apv0 is the result of a propagation in CR3BP normalized model.
400      * @param apv0 Normalized AbsolutePVCoordinates in the rotating frame
401      * @param initialDate Date of the at the beginning of the propagation
402      * @param outputFrame Frame in which the output AbsolutePVCoordinates will be
403      * @return AbsolutePVCoordinates in the output frame [m,m/s]
404      */
405     public AbsolutePVCoordinates getRealAPV(final AbsolutePVCoordinates apv0, final AbsoluteDate initialDate, final Frame outputFrame) {
406 
407         final double duration = apv0.getDate().durationFrom(initialDate) * tDim / (2 * FastMath.PI);
408         final AbsoluteDate date = initialDate.shiftedBy(duration);
409 
410         // PVCoordinate in the output frame
411         final PVCoordinates pv3 = getRealPV(apv0.getPVCoordinates(), date, outputFrame);
412 
413         return new AbsolutePVCoordinates(outputFrame, date, pv3);
414     }
415 
416 }