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.forces.radiation;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.hipparchus.util.FastMath;
27  import org.hipparchus.util.FieldSinCos;
28  import org.hipparchus.util.SinCos;
29  import org.orekit.propagation.FieldSpacecraftState;
30  import org.orekit.propagation.SpacecraftState;
31  import org.orekit.utils.ExtendedPVCoordinatesProvider;
32  import org.orekit.utils.ParameterDriver;
33  
34  /**
35   * The Empirical CODE Orbit Model 2 (ECOM2) of the Center for Orbit Determination in Europe (CODE).
36   * <p>
37   * The drag acceleration is computed as follows :
38   * γ = γ<sub>0</sub> + D(u)e<sub>D</sub> + Y(u)e<sub>Y</sub> + B(u)e<sub>B</sub>
39   * </p> <p>
40   * In the above equation, γ<sub>0</sub> is a selectable a priori model. Since 2013, no
41   * a priori model is used for CODE IGS contribution (i.e. γ<sub>0</sub> = 0). Moreover,
42   * u denotes the satellite's argument of latitude.
43   * </p> <p>
44   * D(u), Y(u) and B(u) are three functions of the ECOM2 model that can be represented
45   * as Fourier series. The coefficients of the Fourier series are estimated during the
46   * estimation process. he ECOM2 model has user-defines upper limits <i>nD</i> and
47   * <i>nB</i> for the Fourier series (i.e. <i>nD</i> for D(u) and <i>nB</i> for
48   * B(u). Y(u) is defined as a constant value).
49   * </p> <p>
50   * It exists several configurations to initialize <i>nD</i> and <i>nB</i> values. However,
51   * Arnold et al recommend to use <b>D2B1</b> (i.e. <i>nD</i> = 1 and <i>nB</i> = 1) and
52   * <b>D4B1</b> (i.e. <i>nD</i> = 2 an <i>nB</i> = 1) configurations. At the opposite, in Arnold paper, it
53   * is recommend to not use <b>D2B0</b> (i.e. <i>nD</i> = 1 and <i>nB</i> = 0) configuration.
54   * </p> <p>
55   * Since Orekit 11.0, it is possible to take into account
56   * the eclipses generated by Moon in the solar radiation
57   * pressure force model using the
58   * {@link #addOccultingBody(ExtendedPVCoordinatesProvider, double)}
59   * method.<br>
60   * <code> ECOM2 srp =</code>
61   * <code>       new ECOM2(1, 1, 0.0, CelestialBodyFactory.getSun(), Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS);</code><br>
62   * <code> srp.addOccultingBody(CelestialBodyFactory.getMoon(), Constants.MOON_EQUATORIAL_RADIUS);</code><br>
63   *
64   * @see "Arnold, Daniel, et al, CODE’s new solar radiation pressure model for GNSS orbit determination,
65   *       Journal of geodesy 89.8 (2015): 775-791."
66   *
67   * @see "Tzu-Pang tseng and Michael Moore, Impact of solar radiation pressure mis-modeling on
68   *       GNSS satellite orbit determination, IGS Worshop, Wuhan, China, 2018."
69   *
70   * @author David Soulard
71   * @since 10.2
72   */
73  public class ECOM2 extends AbstractRadiationForceModel {
74  
75      /** Parameter name for ECOM model coefficients enabling Jacobian processing. */
76      public static final String ECOM_COEFFICIENT = "ECOM coefficient";
77  
78      /** Minimum value for ECOM2 estimated parameters. */
79      private static final double MIN_VALUE = Double.NEGATIVE_INFINITY;
80  
81      /** Maximum value for ECOM2 estimated parameters. */
82      private static final double MAX_VALUE = Double.POSITIVE_INFINITY;
83  
84      /** Parameters scaling factor.
85       * <p>
86       * We use a power of 2 to avoid numeric noise introduction
87       * in the multiplications/divisions sequences.
88       * </p>
89       */
90      private final double SCALE = FastMath.scalb(1.0, -22);
91  
92      /** Highest order for parameter along eD axis (satellite --> sun direction). */
93      private final int nD;
94  
95      /** Highest order for parameter along eB axis. */
96      private final int nB;
97  
98      /** Estimated acceleration coefficients.
99       * <p>
100      * The 2 * nD first driver are Fourier driver along eD, axis,
101      * then along eY, then 2*nB following are along eB axis.
102      * </p>
103      */
104     private final List<ParameterDriver> coefficients;
105 
106     /** Sun model. */
107     private final ExtendedPVCoordinatesProvider sun;
108 
109     /**
110      * Constructor.
111      * @param nD truncation rank of Fourier series in D term.
112      * @param nB truncation rank of Fourier series in B term.
113      * @param value parameters initial value
114      * @param sun provide for Sun parameter
115      * @param equatorialRadius spherical shape model (for umbra/penumbra computation)
116      */
117     public ECOM2(final int nD, final int nB, final double value,
118                  final ExtendedPVCoordinatesProvider sun, final double equatorialRadius) {
119         super(sun, equatorialRadius);
120         this.nB = nB;
121         this.nD = nD;
122         this.coefficients = new ArrayList<>(2 * (nD + nB) + 3);
123 
124         // Add parameter along eB axis in alphabetical order
125         coefficients.add(new ParameterDriver(ECOM_COEFFICIENT + " B0", value, SCALE, MIN_VALUE, MAX_VALUE));
126         for (int i = 1; i < nB + 1; i++) {
127             coefficients.add(new ParameterDriver(ECOM_COEFFICIENT + " Bcos" + Integer.toString(i - 1), value, SCALE, MIN_VALUE, MAX_VALUE));
128         }
129         for (int i = nB + 1; i < 2 * nB + 1; i++) {
130             coefficients.add(new ParameterDriver(ECOM_COEFFICIENT + " Bsin" + Integer.toString(i - (nB + 1)), value, SCALE, MIN_VALUE, MAX_VALUE));
131         }
132         // Add driver along eD axis in alphabetical order
133         coefficients.add(2 * nB + 1, new ParameterDriver(ECOM_COEFFICIENT + " D0", value, SCALE, MIN_VALUE, MAX_VALUE));
134         for (int i = 2 * nB + 2; i < 2 * nB + 2 + nD; i++) {
135             coefficients.add(new ParameterDriver(ECOM_COEFFICIENT + " Dcos" + Integer.toString(i - (2 * nB + 2)), value, SCALE, MIN_VALUE, MAX_VALUE));
136         }
137         for (int i = 2 * nB + 2 + nD; i < 2 * (nB + nD) + 2; i++) {
138             coefficients.add(new ParameterDriver(ECOM_COEFFICIENT + " Dsin" + Integer.toString(i - (2 * nB + nD + 2)), value, SCALE, MIN_VALUE, MAX_VALUE));
139         }
140         // Add  Y0
141         coefficients.add(new ParameterDriver(ECOM_COEFFICIENT + " Y0", value, SCALE, MIN_VALUE, MAX_VALUE));
142 
143         // For ECOM2 model, all parameters are estimated
144         coefficients.forEach(parameter -> parameter.setSelected(true));
145         this.sun = sun;
146     }
147 
148     /** {@inheritDoc} */
149     @Override
150     public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
151         // Build the coordinate system
152         final Vector3D Z = s.getPVCoordinates().getMomentum().normalize();
153         final Vector3D sunPos = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition().normalize();
154         final Vector3D Y = Z.crossProduct(sunPos);
155         final Vector3D X = Y.crossProduct(Z);
156         // Build eD, eY, eB vectors
157         final Vector3D position = s.getPVCoordinates().getPosition().normalize();
158         final Vector3D eD = sunPos.add(-1.0, position);
159         final Vector3D eY = position.crossProduct(eD);
160         final Vector3D eB = eD.crossProduct(eY);
161 
162         // Angular argument difference u_s - u
163         final double  delta_u =  FastMath.atan2(position.dotProduct(Y), position.dotProduct(X));
164 
165         // Compute B(u)
166         double b_u = parameters[0];
167         for (int i = 1; i < nB + 1; i++) {
168             final SinCos sc = FastMath.sinCos(2 * i * delta_u);
169             b_u += parameters[i] * sc.cos() + parameters[i + nB] * sc.sin();
170         }
171         // Compute D(u)
172         double d_u = parameters[2 * nB + 1];
173         for (int i = 1; i < nD + 1; i++) {
174             final SinCos sc = FastMath.sinCos((2 * i - 1) * delta_u);
175             d_u += parameters[2 * nB + 1 + i] * sc.cos() + parameters[2 * nB + 1 + i + nD] * sc.sin();
176         }
177         // Return acceleration
178         return new Vector3D(d_u, eD, parameters[2 * (nD + nB) + 2], eY, b_u, eB);
179     }
180 
181     /** {@inheritDoc} */
182     @Override
183     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s, final T[] parameters) {
184         // Build the coordinate system
185         final FieldVector3D<T> Z = s.getPVCoordinates().getMomentum().normalize();
186         final FieldVector3D<T> sunPos = sun.getPVCoordinates(s.getDate(), s.getFrame()).getPosition().normalize();
187         final FieldVector3D<T> Y = Z.crossProduct(sunPos);
188         final FieldVector3D<T> X = Y.crossProduct(Z);
189 
190         // Build eD, eY, eB vectors
191         final FieldVector3D<T> position = s.getPVCoordinates().getPosition().normalize();
192         final FieldVector3D<T> eD = sunPos.add(-1.0, position);
193         final FieldVector3D<T> eY = position.crossProduct(eD);
194         final FieldVector3D<T> eB = eD.crossProduct(eY);
195 
196         // Angular argument difference u_s - u
197         final T  delta_u = FastMath.atan2(position.dotProduct(Y), position.dotProduct(X));
198 
199         // Compute B(u)
200         T b_u =  parameters[0];
201         for (int i = 1; i < nB + 1; i++) {
202             final FieldSinCos<T> sc = FastMath.sinCos(delta_u.multiply(2 * i));
203             b_u = b_u.add(sc.cos().multiply(parameters[i])).add(sc.sin().multiply(parameters[i + nB]));
204         }
205         // Compute D(u)
206         T d_u = parameters[2 * nB + 1];
207 
208         for (int i = 1; i < nD + 1; i++) {
209             final FieldSinCos<T> sc = FastMath.sinCos(delta_u.multiply(2 * i - 1));
210             d_u =  d_u.add(sc.cos().multiply(parameters[2 * nB + 1 + i])).add(sc.sin().multiply(parameters[2 * nB + 1 + i + nD]));
211         }
212         // Return the acceleration
213         return new FieldVector3D<>(d_u, eD, parameters[2 * (nD + nB) + 2], eY, b_u, eB);
214     }
215 
216     /** {@inheritDoc} */
217     @Override
218     public List<ParameterDriver> getParametersDrivers() {
219         return Collections.unmodifiableList(coefficients);
220     }
221 
222 }
223