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