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.models.earth.troposphere;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.NavigableSet;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.util.MathArrays;
25  import org.orekit.annotation.DefaultDataContext;
26  import org.orekit.bodies.FieldGeodeticPoint;
27  import org.orekit.bodies.GeodeticPoint;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.FieldAbsoluteDate;
30  import org.orekit.time.TimeScale;
31  import org.orekit.time.TimeScalesFactory;
32  import org.orekit.utils.ParameterDriver;
33  import org.orekit.utils.TimeSpanMap;
34  import org.orekit.utils.TimeSpanMap.Transition;
35  
36  /**
37   * Time span estimated tropospheric model.
38   * <p>
39   * This class is closely related to {@link org.orekit.models.earth.troposphere EstimatedTroposphericModel} class.<br>
40   * The difference is that it has a {@link TimeSpanMap} of {@link EstimatedTroposphericModel} objects as attribute. <br>
41   * The idea behind this model is to allow the user to design a tropospheric model that can see its physical parameters
42   * (total zenith delay) change with time, at dates chosen by the user. <br>
43   * </p>
44   * @author Bryan Cazabonne
45   * @since 10.2
46   */
47  public class TimeSpanEstimatedTroposphericModel implements DiscreteTroposphericModel {
48  
49      /** Prefix for dates before in the tropospheric parameter drivers' name. */
50      public static final String DATE_BEFORE = " - Before ";
51  
52      /** Prefix for dates after in the tropospheric parameter drivers' name. */
53      public static final String DATE_AFTER = " - After ";
54  
55      /** Time scale for transition dates. */
56      private final TimeScale timeScale;
57  
58      /** It contains all the models use for the whole period of measurements. */
59      private final TimeSpanMap<EstimatedTroposphericModel> troposphericModelMap;
60  
61      /**
62       * Constructor with default UTC time scale.
63       * @param model the initial model which going to be used for all the models initialization.
64       */
65      @DefaultDataContext
66      public TimeSpanEstimatedTroposphericModel(final EstimatedTroposphericModel model) {
67          this(model, TimeScalesFactory.getUTC());
68      }
69  
70      /**
71       * Constructor with default UTC time scale.
72       * @param model the initial model which going to be used for all the models initialization.
73       * @param timeScale  timeScale Time scale used for the default names of the tropospheric parameter drivers
74       */
75      public TimeSpanEstimatedTroposphericModel(final EstimatedTroposphericModel model,
76                                                final TimeScale timeScale) {
77          this.troposphericModelMap = new TimeSpanMap<>(model);
78          this.timeScale            = timeScale;
79      }
80  
81      /** {@inheritDoc}
82       * <p>
83       * All the parameter drivers of all Estimated models are returned in an array.
84       * Models are ordered chronologically.
85       * </p>
86       */
87      @Override
88      public List<ParameterDriver> getParametersDrivers() {
89  
90          // Get all transitions from the TimeSpanMap
91          final List<ParameterDriver> listTroposphericParameterDrivers = new ArrayList<>();
92          final NavigableSet<Transition<EstimatedTroposphericModel>> troposphericModelTransitions =  getTransitions();
93  
94          // Loop on the transitions
95          for (Transition<EstimatedTroposphericModel> transition : troposphericModelTransitions) {
96              // Add all the "before" parameter drivers of each transition
97              for (ParameterDriver tropoDriver : transition.getBefore().getParametersDrivers()) {
98                  // Add the driver only if the name does not exist already
99                  if (!findByName(listTroposphericParameterDrivers, tropoDriver.getName())) {
100                     listTroposphericParameterDrivers.add(tropoDriver);
101                 }
102             }
103         }
104         // Finally, add the "after" parameter drivers of the last transition
105         for (ParameterDriver tropoDriver : troposphericModelTransitions.last().getAfter().getParametersDrivers()) {
106             // Adds only if the name does not exist already
107             if (!findByName(listTroposphericParameterDrivers, tropoDriver.getName())) {
108                 listTroposphericParameterDrivers.add(tropoDriver);
109             }
110         }
111 
112         // Return an array of parameter drivers with no duplicated name
113         return listTroposphericParameterDrivers;
114 
115     }
116 
117     /** Add an EstimatedTroposphericModel entry valid before a limit date.<br>
118      * Using <code>addTroposphericValidBefore(entry, t)</code> will make <code>entry</code>
119      * valid in ]-∞, t[ (note the open bracket).
120      * @param model EstimatedTroposphericModel entry
121      * @param latestValidityDate date before which the entry is valid
122      * (must be different from <b>all</b> dates already used for transitions)
123      */
124     public void addTroposphericModelValidBefore(final EstimatedTroposphericModel model, final AbsoluteDate latestValidityDate) {
125         troposphericModelMap.addValidBefore(changeTroposphericParameterDriversNames(model,
126                                                                                     latestValidityDate,
127                                                                                     DATE_BEFORE),
128                                             latestValidityDate);
129     }
130 
131     /** Add a EstimatedTroposphericModel entry valid after a limit date.<br>
132      * Using <code>addTroposphericModelValidAfter(entry, t)</code> will make <code>entry</code>
133      * valid in [t, +∞[ (note the closed bracket).
134      * @param model EstimatedTroposphericModel entry
135      * @param earliestValidityDate date after which the entry is valid
136      * (must be different from <b>all</b> dates already used for transitions)
137      */
138     public void addTroposphericModelValidAfter(final EstimatedTroposphericModel model, final AbsoluteDate earliestValidityDate) {
139         troposphericModelMap.addValidAfter(changeTroposphericParameterDriversNames(model,
140                                                                                    earliestValidityDate,
141                                                                                    DATE_AFTER),
142                                            earliestValidityDate);
143     }
144 
145     /** Get the {@link EstimatedTroposphericModel} model valid at a date.
146      * @param date the date of validity
147      * @return the EstimatedTroposphericModel model valid at date
148      */
149     public EstimatedTroposphericModel getTroposphericModel(final AbsoluteDate date) {
150         return troposphericModelMap.get(date);
151     }
152 
153     /** Get the {@link Transition}s of the tropospheric model time span map.
154      * @return the {@link Transition}s for the tropospheric model time span map
155      */
156     public NavigableSet<Transition<EstimatedTroposphericModel>> getTransitions() {
157         return troposphericModelMap.getTransitions();
158     }
159 
160     /** Extract the proper parameter drivers' values from the array in input of the
161      * {@link #pathDelay(double, GeodeticPoint, double[], AbsoluteDate) pathDelay} method.
162      *  Parameters are filtered given an input date.
163      * @param parameters the input parameters array
164      * @param date the date
165      * @return the parameters given the date
166      */
167     public double[] extractParameters(final double[] parameters, final AbsoluteDate date) {
168 
169         // Get the tropospheric parameter drivers of the date
170         final List<ParameterDriver> troposphericParameterDriver = getTroposphericModel(date).getParametersDrivers();
171 
172         // Find out the indexes of the parameters in the whole array of parameters
173         final List<ParameterDriver> allTroposphericParameters = getParametersDrivers();
174         final double[] outParameters = new double[troposphericParameterDriver.size()];
175         int index = 0;
176         for (int i = 0; i < allTroposphericParameters.size(); i++) {
177             final String driverName = allTroposphericParameters.get(i).getName();
178             for (ParameterDriver tropoDriver : troposphericParameterDriver) {
179                 if (tropoDriver.getName().equals(driverName)) {
180                     outParameters[index++] = parameters[i];
181                 }
182             }
183         }
184         return outParameters;
185     }
186 
187     /** Extract the proper parameter drivers' values from the array in input of the
188      * {@link #pathDelay(double, GeodeticPoint, double[], AbsoluteDate) pathDelay} method.
189      *  Parameters are filtered given an input date.
190      * @param parameters the input parameters array
191      * @param date the date
192      * @param <T> extends CalculusFieldElements
193      * @return the parameters given the date
194      */
195     public <T extends CalculusFieldElement<T>> T[] extractParameters(final T[] parameters,
196                                                                  final FieldAbsoluteDate<T> date) {
197 
198         // Get the tropospheric parameter drivers of the date
199         final List<ParameterDriver> troposphericParameterDriver = getTroposphericModel(date.toAbsoluteDate()).getParametersDrivers();
200 
201         // Find out the indexes of the parameters in the whole array of parameters
202         final List<ParameterDriver> allTroposphericParameters = getParametersDrivers();
203         final T[] outParameters = MathArrays.buildArray(date.getField(), troposphericParameterDriver.size());
204         int index = 0;
205         for (int i = 0; i < allTroposphericParameters.size(); i++) {
206             final String driverName = allTroposphericParameters.get(i).getName();
207             for (ParameterDriver tropoDriver : troposphericParameterDriver) {
208                 if (tropoDriver.getName().equals(driverName)) {
209                     outParameters[index++] = parameters[i];
210                 }
211             }
212         }
213         return outParameters;
214     }
215 
216     /** {@inheritDoc} */
217     @Override
218     public double pathDelay(final double elevation, final GeodeticPoint point,
219                             final double[] parameters, final AbsoluteDate date) {
220         // Extract the proper parameters valid at date from the input array
221         final double[] extractedParameters = extractParameters(parameters, date);
222         // Compute and return the path delay
223         return getTroposphericModel(date).pathDelay(elevation, point,
224                                                     extractedParameters, date);
225     }
226 
227     /** {@inheritDoc} */
228     @Override
229     public <T extends CalculusFieldElement<T>> T pathDelay(final T elevation, final  FieldGeodeticPoint<T> point,
230                                                        final T[] parameters, final FieldAbsoluteDate<T> date) {
231         // Extract the proper parameters valid at date from the input array
232         final T[] extractedParameters = extractParameters(parameters, date);
233         // Compute and return the path delay
234         return getTroposphericModel(date.toAbsoluteDate()).pathDelay(elevation, point,
235                                                                      extractedParameters, date);
236     }
237 
238     /** Find if a parameter driver with a given name already exists in a list of parameter drivers.
239      * @param driversList the list of parameter drivers
240      * @param name the parameter driver's name to filter with
241      * @return true if the name was found, false otherwise
242      */
243     private boolean findByName(final List<ParameterDriver> driversList, final String name) {
244         for (final ParameterDriver driver : driversList) {
245             if (driver.getName().equals(name)) {
246                 return true;
247             }
248         }
249         return false;
250     }
251 
252     /** Change the parameter drivers names of a {@link EstimatedTroposphericModel} model, if needed.
253      * <p>
254      * This is done to avoid that several parameter drivers have the same name.<br>
255      * It is done only if the user hasn't modify the EstimatedTroposphericModel parameter drivers default names.
256      * </p>
257      * @param troposphericModel the EstimatedTroposphericModel model
258      * @param date the date used in the parameter driver's name
259      * @param datePrefix the date prefix used in the parameter driver's name
260      * @return the EstimatedTroposphericModel with its drivers' names changed
261      */
262     private EstimatedTroposphericModel changeTroposphericParameterDriversNames(final EstimatedTroposphericModel troposphericModel,
263                                                                                final AbsoluteDate date,
264                                                                                final String datePrefix) {
265         // Loop on the parameter drivers of the EstimatedTroposphericModel model
266         for (ParameterDriver driver: troposphericModel.getParametersDrivers()) {
267             final String driverName = driver.getName();
268 
269             // If the name is the default name for EstimatedTroposphericModel parameter drivers
270             // Modify the name to add the prefix and the date
271             if (driverName.equals(EstimatedTroposphericModel.TOTAL_ZENITH_DELAY)) {
272                 driver.setName(driverName + datePrefix + date.toString(timeScale));
273             }
274         }
275         return troposphericModel;
276     }
277 
278 }