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.empirical;
18  
19  import java.util.List;
20  import java.util.stream.Stream;
21  
22  import org.hipparchus.Field;
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.orekit.attitudes.Attitude;
27  import org.orekit.attitudes.AttitudeProvider;
28  import org.orekit.attitudes.FieldAttitude;
29  import org.orekit.forces.AbstractForceModel;
30  import org.orekit.propagation.FieldSpacecraftState;
31  import org.orekit.propagation.SpacecraftState;
32  import org.orekit.propagation.events.EventDetector;
33  import org.orekit.propagation.events.FieldEventDetector;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.utils.ParameterDriver;
36  
37  /** This class implements a parametric acceleration.
38   * <p>Parametric accelerations are intended to model lesser-known
39   * forces, estimating a few defining parameters from a parametric
40   * function using orbit determination. Typical parametric functions
41   * are polynomial (often limited to a constant term) and harmonic
42   * (often with either orbital period or half orbital period).</p>
43   * <p>An important operational example is the infamous GPS Y-bias,
44   * which is thought to be related to a radiator thermal radiation.
45   * Other examples could be to model leaks that produce roughly constant
46   * trust in some spacecraft-related direction.</p>
47   * <p>The acceleration direction is considered constant in either:
48   * </p>
49   * <ul>
50   *   <li>inertial frame</li>
51   *   <li>spacecraft frame</li>
52   *   <li>a dedicated attitude frame overriding spacecraft attitude
53   *   (this could for example be used to model solar arrays orientation
54   *   if the force is related to solar arrays)</li>
55   * </ul>
56   * <p>
57   * If the direction of the acceleration is unknown, then three instances
58   * of this class should be used, one along the X axis, one along the Y
59   * axis and one along the Z axis and their parameters estimated as usual.
60   * </p>
61   * @since 10.3
62   * @author Luc Maisonobe
63   * @author Bryan Cazabonne
64   */
65  public class ParametricAcceleration extends AbstractForceModel {
66  
67      /** Direction of the acceleration in defining frame. */
68      private final Vector3D direction;
69  
70      /** Flag for inertial acceleration direction. */
71      private final boolean isInertial;
72  
73      /** The attitude to override, if set. */
74      private final AttitudeProvider attitudeOverride;
75  
76      /** Acceleration model. */
77      private final AccelerationModel accelerationModel;
78  
79      /** Simple constructor.
80       * @param direction acceleration direction in overridden spacecraft frame
81       * @param isInertial if true, direction is defined in the same inertial
82       * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
83       * otherwise direction is defined in spacecraft frame (i.e. using the
84       * propagation {@link
85       * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
86       * attitude law})
87       * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
88       * direction
89       */
90      public ParametricAcceleration(final Vector3D direction,
91                                    final boolean isInertial,
92                                    final AccelerationModel accelerationModel) {
93          this(direction, isInertial, null, accelerationModel);
94      }
95  
96      /** Simple constructor.
97       * @param direction acceleration direction in overridden spacecraft frame
98       * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
99       * otherwise direction is defined in spacecraft frame (i.e. using the
100      * propagation {@link
101      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
102      * attitude law})
103      * @param attitudeOverride provider for attitude used to compute acceleration
104      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
105      * direction
106      */
107     public ParametricAcceleration(final Vector3D direction,
108                                   final AttitudeProvider attitudeOverride,
109                                   final AccelerationModel accelerationModel) {
110         this(direction, false, attitudeOverride, accelerationModel);
111     }
112 
113     /** Simple constructor.
114      * @param direction acceleration direction in overridden spacecraft frame
115      * @param isInertial if true, direction is defined in the same inertial
116      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
117      * otherwise direction is defined in spacecraft frame (i.e. using the
118      * propagation {@link
119      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
120      * attitude law})
121      * @param attitudeOverride provider for attitude used to compute acceleration
122      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
123      * direction
124      */
125     private ParametricAcceleration(final Vector3D direction,
126                                    final boolean isInertial,
127                                    final AttitudeProvider attitudeOverride,
128                                    final AccelerationModel accelerationModel) {
129         this.direction         = direction;
130         this.isInertial        = isInertial;
131         this.attitudeOverride  = attitudeOverride;
132         this.accelerationModel = accelerationModel;
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     public boolean dependsOnPositionOnly() {
138         return isInertial;
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public List<ParameterDriver> getParametersDrivers() {
144         return accelerationModel.getParametersDrivers();
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
150         accelerationModel.init(initialState, target);
151     }
152 
153     /** {@inheritDoc} */
154     @Override
155     public Vector3D acceleration(final SpacecraftState state,
156                                  final double[] parameters) {
157 
158         final Vector3D inertialDirection;
159         if (isInertial) {
160             // the acceleration direction is already defined in the inertial frame
161             inertialDirection = direction;
162         } else {
163             final Attitude attitude;
164             if (attitudeOverride == null) {
165                 // the acceleration direction is defined in spacecraft frame as set by the propagator
166                 attitude = state.getAttitude();
167             } else {
168                 // the acceleration direction is defined in a dedicated frame
169                 attitude = attitudeOverride.getAttitude(state.getOrbit(), state.getDate(), state.getFrame());
170             }
171             inertialDirection = attitude.getRotation().applyInverseTo(direction);
172         }
173 
174         // Call the acceleration model to compute the acceleration
175         return new Vector3D(accelerationModel.signedAmplitude(state, parameters), inertialDirection);
176 
177     }
178 
179     /** {@inheritDoc} */
180     @Override
181     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> state,
182                                                                          final T[] parameters) {
183 
184         final FieldVector3D<T> inertialDirection;
185         if (isInertial) {
186             // the acceleration direction is already defined in the inertial frame
187             inertialDirection = new FieldVector3D<>(state.getDate().getField(), direction);
188         } else {
189             final FieldAttitude<T> attitude;
190             if (attitudeOverride == null) {
191                 // the acceleration direction is defined in spacecraft frame as set by the propagator
192                 attitude = state.getAttitude();
193             } else {
194                 // the acceleration direction is defined in a dedicated frame
195                 attitude = attitudeOverride.getAttitude(state.getOrbit(), state.getDate(), state.getFrame());
196             }
197             inertialDirection = attitude.getRotation().applyInverseTo(direction);
198         }
199 
200         // Call the acceleration model to compute the acceleration
201         return new FieldVector3D<>(accelerationModel.signedAmplitude(state, parameters), inertialDirection);
202 
203     }
204 
205     /** {@inheritDoc} */
206     @Override
207     public Stream<EventDetector> getEventsDetectors() {
208         return Stream.empty();
209     }
210 
211     /** {@inheritDoc} */
212     @Override
213     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
214         return Stream.empty();
215     }
216 
217 }