1   /* Copyright 2022-2026 Romain Serra
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.orbits;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.FieldSinCos;
23  import org.orekit.errors.OrekitIllegalArgumentException;
24  import org.orekit.errors.OrekitMessages;
25  import org.orekit.utils.FieldPVCoordinates;
26  
27  /**
28   * Class for converting between equinoctial elements and Cartesian coordinates (Field version).
29   * @param <T> type of the field element
30   * @author Romain Serra
31   * @see FieldEquinoctialParameters
32   * @see EquinoctialParametersConverter
33   * @since 14.0
34   */
35  public class FieldEquinoctialParametersConverter<T extends CalculusFieldElement<T>> {
36  
37      /** Central body gravitational parameter. */
38      private final T mu;
39  
40      /**
41       * Constructor.
42       * @param mu central body gravitational parameter
43       */
44      public FieldEquinoctialParametersConverter(final T mu) {
45          this.mu = mu;
46      }
47  
48      /**
49       * Convert Cartesian coordinates to equinoctial elements.
50       * @param cartesian position and velocity in inertial frame
51       * @param positionAngleType type of position angle to use
52       * @return equinoctial elements
53       */
54      public FieldEquinoctialParameters<T> toParameters(final FieldPVCoordinates<T> cartesian,
55                                                        final PositionAngleType positionAngleType) {
56          // compute semi-major axis
57          final FieldVector3D<T> pvP = cartesian.getPosition();
58          final T r = pvP.getNorm2();
59          final T v2 = cartesian.getVelocity().getNorm2Sq();
60          final T rV2OnMu = r.multiply(v2).divide(mu);
61          final T a = r.divide(rV2OnMu.negate().add(2));
62  
63          if (a.getReal() < 0.) {
64              throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
65                      getClass().getName());
66          }
67  
68          // compute inclination vector
69          final FieldVector3D<T> w = cartesian.getMomentum().normalize();
70          final T one = a.getField().getOne();
71          final T d = one.divide(one.add(w.getZ()));
72          final T hx =  d.negate().multiply(w.getY());
73          final T hy =  d.multiply(w.getX());
74  
75          // compute true longitude argument
76          final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r);
77          final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r);
78          final T trueLongitude = sLv.atan2(cLv);
79  
80          // compute eccentricity vector
81          final T eSE = FieldVector3D.dotProduct(pvP, cartesian.getVelocity()).divide(a.multiply(mu).sqrt());
82          final T eCE = rV2OnMu.subtract(1);
83          final T e2  = eCE.square().add(eSE.square());
84          final T f   = eCE.subtract(e2);
85          final T g   = e2.negate().add(1).sqrt().multiply(eSE);
86          final T ex = a.multiply(f.multiply(cLv).add( g.multiply(sLv))).divide(r);
87          final T ey = a.multiply(f.multiply(sLv).subtract(g.multiply(cLv))).divide(r);
88  
89          final FieldEquinoctialParameters<T> equinoctialParameters = new FieldEquinoctialParameters<>(a, ex, ey, hx, hy,
90                  trueLongitude, PositionAngleType.TRUE);
91          return positionAngleType == PositionAngleType.TRUE ? equinoctialParameters :
92                  equinoctialParameters.withPositionAngleType(positionAngleType);
93      }
94  
95      /**
96       * Convert equinoctial elements to Cartesian coordinates.
97       * @param elements equinoctial elements
98       * @return position and velocity in inertial frame
99       */
100     public FieldPVCoordinates<T> toCartesian(final FieldEquinoctialParameters<T> elements) {
101         // get equinoctial parameters
102         final T a = elements.a();
103         final T ex = elements.ex();
104         final T ey = elements.ey();
105         final T hx = elements.hx();
106         final T hy = elements.hy();
107         final T lE = elements.positionAngleType() == PositionAngleType.ECCENTRIC ? elements.longitudeArgument() :
108                 elements.withPositionAngleType(PositionAngleType.ECCENTRIC).longitudeArgument();
109 
110         // inclination-related intermediate parameters
111         final T hx2   = hx.square();
112         final T hy2   = hy.square();
113         final T one = a.getField().getOne();
114         final T factH = one.divide(hx2.add(1.0).add(hy2));
115 
116         // reference axes defining the orbital plane
117         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
118         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
119         final T uz = hy.multiply(-2).multiply(factH);
120 
121         final T vx = uy;
122         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
123         final T vz =  hx.multiply(factH).multiply(2);
124 
125         // eccentricity-related intermediate parameters
126         final T ex2  = ex.square();
127         final T exey = ex.multiply(ey);
128         final T ey2  = ey.square();
129         final T e2   = ex2.add(ey2);
130         final T eta  = one.subtract(e2).sqrt().add(1);
131         final T beta = one.divide(eta);
132 
133         // eccentric longitude argument
134         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
135         final T cLe    = scLe.cos();
136         final T sLe    = scLe.sin();
137         final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe));
138 
139         // coordinates of position and velocity in the orbital plane
140         final T x      = a.multiply(one.subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex));
141         final T y      = a.multiply(one.subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey));
142 
143         final T factor = mu.divide(a).sqrt().divide(one.subtract(exCeyS));
144         final T xdot   = factor.multiply(sLe.negate().add(beta.multiply(ey).multiply(exCeyS)));
145         final T ydot   = factor.multiply(cLe.subtract(beta.multiply(ex).multiply(exCeyS)));
146 
147         final FieldVector3D<T> position =
148                 new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
149                         x.multiply(uy).add(y.multiply(vy)),
150                         x.multiply(uz).add(y.multiply(vz)));
151         final FieldVector3D<T> velocity =
152                 new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)), xdot.multiply(uy).add(ydot.multiply(vy)), xdot.multiply(uz).add(ydot.multiply(vz)));
153 
154         return new FieldPVCoordinates<>(position, velocity);
155     }
156 }