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