1   /* Copyright 2002-2019 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (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;
18  
19  import java.util.Collections;
20  import java.util.List;
21  
22  import org.hipparchus.Field;
23  import org.hipparchus.RealFieldElement;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.MathArrays;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.time.DateTimeComponents;
28  import org.orekit.time.FieldAbsoluteDate;
29  import org.orekit.time.TimeScalesFactory;
30  import org.orekit.utils.ParameterDriver;
31  
32  /** The Vienna1 tropospheric delay model for radio techniques.
33   * The Vienna model data are given with a time interval of 6 hours
34   * as well as on a global 2.5° * 2.0° grid.
35   *
36   * This version considered the height correction for the hydrostatic part
37   * developed by Niell, 1996.
38   *
39   * @see Boehm, J., Werl, B., and Schuh, H., (2006),
40   *      "Troposhere mapping functions for GPS and very long baseline
41   *      interferometry from European Centre for Medium-Range Weather
42   *      Forecasts operational analysis data," J. Geophy. Res., Vol. 111,
43   *      B02406, doi:10.1029/2005JB003629
44   *
45   * @author Bryan Cazabonne
46   */
47  public class ViennaOneModel implements DiscreteTroposphericModel {
48  
49      /** Serializable UID. */
50      private static final long serialVersionUID = 2584920506094034855L;
51  
52      /** The a coefficient for the computation of the wet and hydrostatic mapping functions.*/
53      private final double[] coefficientsA;
54  
55      /** Values of hydrostatic and wet delays as provided by the Vienna model. */
56      private final double[] zenithDelay;
57  
58      /** Geodetic site latitude, radians.*/
59      private final double latitude;
60  
61      /** Build a new instance.
62       * @param coefficientA The a coefficients for the computation of the wet and hydrostatic mapping functions.
63       * @param zenithDelay Values of hydrostatic and wet delays
64       * @param latitude geodetic latitude of the station, in radians
65       */
66      public ViennaOneModel(final double[] coefficientA, final double[] zenithDelay,
67                            final double latitude) {
68          this.coefficientsA = coefficientA.clone();
69          this.zenithDelay   = zenithDelay.clone();
70          this.latitude      = latitude;
71      }
72  
73      /** {@inheritDoc} */
74      @Override
75      public double pathDelay(final double elevation, final double height,
76                              final double[] parameters, final AbsoluteDate date) {
77          // zenith delay
78          final double[] delays = computeZenithDelay(height, parameters, date);
79          // mapping function
80          final double[] mappingFunction = mappingFactors(elevation, height, parameters, date);
81          // Tropospheric path delay
82          return delays[0] * mappingFunction[0] + delays[1] * mappingFunction[1];
83      }
84  
85      /** {@inheritDoc} */
86      @Override
87      public <T extends RealFieldElement<T>> T pathDelay(final T elevation, final T height,
88                                                         final T[] parameters, final FieldAbsoluteDate<T> date) {
89          // zenith delay
90          final T[] delays = computeZenithDelay(height, parameters, date);
91          // mapping function
92          final T[] mappingFunction = mappingFactors(elevation, height, parameters, date);
93          // Tropospheric path delay
94          return delays[0].multiply(mappingFunction[0]).add(delays[1].multiply(mappingFunction[1]));
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public double[] computeZenithDelay(final double height, final double[] parameters, final AbsoluteDate date) {
100         return zenithDelay;
101     }
102 
103     /** {@inheritDoc} */
104     @Override
105     public <T extends RealFieldElement<T>> T[] computeZenithDelay(final T height, final T[] parameters,
106                                                                   final FieldAbsoluteDate<T> date) {
107         final Field<T> field = height.getField();
108         final T zero = field.getZero();
109         final T[] delays = MathArrays.buildArray(field, 2);
110         delays[0] = zero.add(zenithDelay[0]);
111         delays[1] = zero.add(zenithDelay[1]);
112         return delays;
113     }
114 
115     /** {@inheritDoc} */
116     @Override
117     public double[] mappingFactors(final double elevation, final double height,
118                                    final double[] parameters, final AbsoluteDate date) {
119         // Day of year computation
120         final DateTimeComponents dtc = date.getComponents(TimeScalesFactory.getUTC());
121         final int dofyear = dtc.getDate().getDayOfYear();
122 
123         // General constants | Hydrostatic part
124         final double bh  = 0.0029;
125         final double c0h = 0.062;
126         final double c10h;
127         final double c11h;
128         final double psi;
129 
130         // sin(latitude) > 0 -> northern hemisphere
131         if (FastMath.sin(latitude) > 0) {
132             c10h = 0.001;
133             c11h = 0.005;
134             psi  = 0;
135         } else {
136             c10h = 0.002;
137             c11h = 0.007;
138             psi  = FastMath.PI;
139         }
140 
141         // Temporal factor
142         double t0 = 28;
143         if (latitude < 0) {
144             // southern hemisphere: t0 = 28 + an integer half of year
145             t0 += 183;
146         }
147         // Compute hydrostatique coefficient c
148         final double coef = ((dofyear - t0) / 365) * 2 * FastMath.PI + psi;
149         final double ch = c0h + ((FastMath.cos(coef) + 1) * (c11h / 2) + c10h) * (1 - FastMath.cos(latitude));
150 
151         // General constants | Wet part
152         final double bw = 0.00146;
153         final double cw = 0.04391;
154 
155         final double[] function = new double[2];
156         function[0] = computeFunction(coefficientsA[0], bh, ch, elevation);
157         function[1] = computeFunction(coefficientsA[1], bw, cw, elevation);
158 
159         // Apply height correction
160         final double correction = computeHeightCorrection(elevation, height);
161         function[0] = function[0] + correction;
162 
163         return function;
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     public <T extends RealFieldElement<T>> T[] mappingFactors(final T elevation, final T height,
169                                                               final T[] parameters, final FieldAbsoluteDate<T> date) {
170         final Field<T> field = date.getField();
171         final T zero = field.getZero();
172 
173         // Day of year computation
174         final DateTimeComponents dtc = date.getComponents(TimeScalesFactory.getUTC());
175         final int dofyear = dtc.getDate().getDayOfYear();
176 
177         // General constants | Hydrostatic part
178         final T bh  = zero.add(0.0029);
179         final T c0h = zero.add(0.062);
180         final T c10h;
181         final T c11h;
182         final T psi;
183 
184         // sin(latitude) > 0 -> northern hemisphere
185         if (FastMath.sin(latitude) > 0) {
186             c10h = zero.add(0.001);
187             c11h = zero.add(0.005);
188             psi  = zero;
189         } else {
190             c10h = zero.add(0.002);
191             c11h = zero.add(0.007);
192             psi  = zero.add(FastMath.PI);
193         }
194 
195         // Compute hydrostatique coefficient c
196         // Temporal factor
197         double t0 = 28;
198         if (latitude < 0) {
199             // southern hemisphere: t0 = 28 + an integer half of year
200             t0 += 183;
201         }
202         final T coef = psi.add(((dofyear - t0) / 365) * 2 * FastMath.PI);
203         final T ch = c11h.divide(2.0).multiply(FastMath.cos(coef).add(1.0)).add(c10h).multiply(1 - FastMath.cos(latitude)).add(c0h);
204 
205         // General constants | Wet part
206         final T bw = zero.add(0.00146);
207         final T cw = zero.add(0.04391);
208 
209         final T[] function = MathArrays.buildArray(field, 2);
210         function[0] = computeFunction(zero.add(coefficientsA[0]), bh, ch, elevation);
211         function[1] = computeFunction(zero.add(coefficientsA[1]), bw, cw, elevation);
212 
213         // Apply height correction
214         final T correction = computeHeightCorrection(elevation, height, field);
215         function[0] = function[0].add(correction);
216 
217         return function;
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public List<ParameterDriver> getParametersDrivers() {
223         return Collections.emptyList();
224     }
225 
226     /** Compute the mapping function related to the coefficient values and the elevation.
227      * @param a a coefficient
228      * @param b b coefficient
229      * @param c c coefficient
230      * @param elevation the elevation of the satellite, in radians.
231      * @return the value of the function at a given elevation
232      */
233     private double computeFunction(final double a, final double b, final double c, final double elevation) {
234         final double sinE = FastMath.sin(elevation);
235         // Numerator
236         final double numMP = 1 + a / (1 + b / (1 + c));
237         // Denominator
238         final double denMP = sinE + a / (sinE + b / (sinE + c));
239 
240         final double felevation = numMP / denMP;
241 
242         return felevation;
243     }
244 
245     /** Compute the mapping function related to the coefficient values and the elevation.
246      * @param <T> type of the elements
247      * @param a a coefficient
248      * @param b b coefficient
249      * @param c c coefficient
250      * @param elevation the elevation of the satellite, in radians.
251      * @return the value of the function at a given elevation
252      */
253     private <T extends RealFieldElement<T>> T computeFunction(final T a, final T b, final T c, final T elevation) {
254         final T sinE = FastMath.sin(elevation);
255         // Numerator
256         final T numMP = a.divide(b.divide(c.add(1.0)).add(1.0)).add(1.0);
257         // Denominator
258         final T denMP = a.divide(b.divide(c.add(sinE)).add(sinE)).add(sinE);
259 
260         final T felevation = numMP.divide(denMP);
261 
262         return felevation;
263     }
264 
265     /** This method computes the height correction for the hydrostatic
266      *  component of the mapping function.
267      *  The formulas are given by Neill's paper, 1996:
268      *<p>
269      *      Niell A. E. (1996)
270      *      "Global mapping functions for the atmosphere delay of radio wavelengths,”
271      *      J. Geophys. Res., 101(B2), pp.  3227–3246, doi:  10.1029/95JB03048.
272      *</p>
273      * @param elevation the elevation of the satellite, in radians.
274      * @param height the height of the station in m above sea level.
275      * @return the height correction, in m
276      */
277     private double computeHeightCorrection(final double elevation, final double height) {
278         final double fixedHeight = FastMath.max(0.0, height);
279         final double sinE = FastMath.sin(elevation);
280         // Ref: Eq. 4
281         final double function = computeFunction(2.53e-5, 5.49e-3, 1.14e-3, elevation);
282         // Ref: Eq. 6
283         final double dmdh = (1 / sinE) - function;
284         // Ref: Eq. 7
285         final double correction = dmdh * (fixedHeight / 1000);
286         return correction;
287     }
288 
289     /** This method computes the height correction for the hydrostatic
290      *  component of the mapping function.
291      *  The formulas are given by Neill's paper, 1996:
292      *<p>
293      *      Niell A. E. (1996)
294      *      "Global mapping functions for the atmosphere delay of radio wavelengths,”
295      *      J. Geophys. Res., 101(B2), pp.  3227–3246, doi:  10.1029/95JB03048.
296      *</p>
297      * @param <T> type of the elements
298      * @param elevation the elevation of the satellite, in radians.
299      * @param height the height of the station in m above sea level.
300      * @param field field to which the elements belong
301      * @return the height correction, in m
302      */
303     private <T extends RealFieldElement<T>> T computeHeightCorrection(final T elevation, final T height, final Field<T> field) {
304         final T zero = field.getZero();
305         final T fixedHeight = FastMath.max(zero, height);
306         final T sinE = FastMath.sin(elevation);
307         // Ref: Eq. 4
308         final T function = computeFunction(zero.add(2.53e-5), zero.add(5.49e-3), zero.add(1.14e-3), elevation);
309         // Ref: Eq. 6
310         final T dmdh = sinE.reciprocal().subtract(function);
311         // Ref: Eq. 7
312         final T correction = dmdh.multiply(fixedHeight.divide(1000.0));
313         return correction;
314     }
315 
316 }