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.Collections;
20  import java.util.List;
21  
22  import org.hipparchus.Field;
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.MathArrays;
26  import org.orekit.annotation.DefaultDataContext;
27  import org.orekit.bodies.FieldGeodeticPoint;
28  import org.orekit.bodies.GeodeticPoint;
29  import org.orekit.data.DataContext;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.DateTimeComponents;
32  import org.orekit.time.FieldAbsoluteDate;
33  import org.orekit.time.TimeScale;
34  import org.orekit.utils.ParameterDriver;
35  
36  /** The Vienna1 tropospheric delay model for radio techniques.
37   * The Vienna model data are given with a time interval of 6 hours
38   * as well as on a global 2.5° * 2.0° grid.
39   *
40   * This version considered the height correction for the hydrostatic part
41   * developed by Niell, 1996.
42   *
43   * @see "Boehm, J., Werl, B., and Schuh, H., (2006),
44   *       Troposhere mapping functions for GPS and very long baseline
45   *       interferometry from European Centre for Medium-Range Weather
46   *       Forecasts operational analysis data, J. Geophy. Res., Vol. 111,
47   *       B02406, doi:10.1029/2005JB003629"
48   *
49   * @author Bryan Cazabonne
50   */
51  public class ViennaOneModel implements DiscreteTroposphericModel, MappingFunction {
52  
53      /** The a coefficient for the computation of the wet and hydrostatic mapping functions.*/
54      private final double[] coefficientsA;
55  
56      /** Values of hydrostatic and wet delays as provided by the Vienna model. */
57      private final double[] zenithDelay;
58  
59      /** UTC time scale. */
60      private final TimeScale utc;
61  
62      /** Build a new instance.
63       *
64       * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
65       *
66       * @param coefficientA The a coefficients for the computation of the wet and hydrostatic mapping functions.
67       * @param zenithDelay Values of hydrostatic and wet delays
68       * @see #ViennaOneModel(double[], double[], TimeScale)
69       */
70      @DefaultDataContext
71      public ViennaOneModel(final double[] coefficientA, final double[] zenithDelay) {
72          this(coefficientA, zenithDelay,
73               DataContext.getDefault().getTimeScales().getUTC());
74      }
75  
76      /**
77       * Build a new instance.
78       *
79       * @param coefficientA The a coefficients for the computation of the wet and
80       *                     hydrostatic mapping functions.
81       * @param zenithDelay  Values of hydrostatic and wet delays
82       * @param utc          UTC time scale.
83       * @since 10.1
84       */
85      public ViennaOneModel(final double[] coefficientA,
86                            final double[] zenithDelay,
87                            final TimeScale utc) {
88          this.coefficientsA = coefficientA.clone();
89          this.zenithDelay   = zenithDelay.clone();
90          this.utc           = utc;
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public double pathDelay(final double elevation, final GeodeticPoint point,
96                              final double[] parameters, final AbsoluteDate date) {
97          // zenith delay
98          final double[] delays = computeZenithDelay(point, parameters, date);
99          // mapping function
100         final double[] mappingFunction = mappingFactors(elevation, point, date);
101         // Tropospheric path delay
102         return delays[0] * mappingFunction[0] + delays[1] * mappingFunction[1];
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public <T extends CalculusFieldElement<T>> T pathDelay(final T elevation, final FieldGeodeticPoint<T> point,
108                                                        final T[] parameters, final FieldAbsoluteDate<T> date) {
109         // zenith delay
110         final T[] delays = computeZenithDelay(point, parameters, date);
111         // mapping function
112         final T[] mappingFunction = mappingFactors(elevation, point, date);
113         // Tropospheric path delay
114         return delays[0].multiply(mappingFunction[0]).add(delays[1].multiply(mappingFunction[1]));
115     }
116 
117     /** This method allows the  computation of the zenith hydrostatic and
118      * zenith wet delay. The resulting element is an array having the following form:
119      * <ul>
120      * <li>T[0] = D<sub>hz</sub> → zenith hydrostatic delay
121      * <li>T[1] = D<sub>wz</sub> → zenith wet delay
122      * </ul>
123      * @param point station location
124      * @param parameters tropospheric model parameters
125      * @param date current date
126      * @return a two components array containing the zenith hydrostatic and wet delays.
127      */
128     public double[] computeZenithDelay(final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) {
129         return zenithDelay.clone();
130     }
131 
132     /** This method allows the  computation of the zenith hydrostatic and
133      * zenith wet delay. The resulting element is an array having the following form:
134      * <ul>
135      * <li>T[0] = D<sub>hz</sub> → zenith hydrostatic delay
136      * <li>T[1] = D<sub>wz</sub> → zenith wet delay
137      * </ul>
138      * @param <T> type of the elements
139      * @param point station location
140      * @param parameters tropospheric model parameters
141      * @param date current date
142      * @return a two components array containing the zenith hydrostatic and wet delays.
143      */
144     public <T extends CalculusFieldElement<T>> T[] computeZenithDelay(final FieldGeodeticPoint<T> point, final T[] parameters,
145                                                                   final FieldAbsoluteDate<T> date) {
146         final Field<T> field = date.getField();
147         final T zero = field.getZero();
148         final T[] delays = MathArrays.buildArray(field, 2);
149         delays[0] = zero.add(zenithDelay[0]);
150         delays[1] = zero.add(zenithDelay[1]);
151         return delays;
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public double[] mappingFactors(final double elevation, final GeodeticPoint point,
157                                    final AbsoluteDate date) {
158         // Day of year computation
159         final DateTimeComponents dtc = date.getComponents(utc);
160         final int dofyear = dtc.getDate().getDayOfYear();
161 
162         // General constants | Hydrostatic part
163         final double bh  = 0.0029;
164         final double c0h = 0.062;
165         final double c10h;
166         final double c11h;
167         final double psi;
168 
169         // Latitude of the station
170         final double latitude = point.getLatitude();
171 
172         // sin(latitude) > 0 -> northern hemisphere
173         if (FastMath.sin(latitude) > 0) {
174             c10h = 0.001;
175             c11h = 0.005;
176             psi  = 0;
177         } else {
178             c10h = 0.002;
179             c11h = 0.007;
180             psi  = FastMath.PI;
181         }
182 
183         // Temporal factor
184         double t0 = 28;
185         if (latitude < 0) {
186             // southern hemisphere: t0 = 28 + an integer half of year
187             t0 += 183;
188         }
189         // Compute hydrostatique coefficient c
190         final double coef = ((dofyear - t0) / 365) * 2 * FastMath.PI + psi;
191         final double ch = c0h + ((FastMath.cos(coef) + 1) * (c11h / 2) + c10h) * (1 - FastMath.cos(latitude));
192 
193         // General constants | Wet part
194         final double bw = 0.00146;
195         final double cw = 0.04391;
196 
197         final double[] function = new double[2];
198         function[0] = TroposphericModelUtils.mappingFunction(coefficientsA[0], bh, ch, elevation);
199         function[1] = TroposphericModelUtils.mappingFunction(coefficientsA[1], bw, cw, elevation);
200 
201         // Apply height correction
202         final double correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude());
203         function[0] = function[0] + correction;
204 
205         return function;
206     }
207 
208     /** {@inheritDoc} */
209     @Override
210     public <T extends CalculusFieldElement<T>> T[] mappingFactors(final T elevation, final FieldGeodeticPoint<T> point,
211                                                               final FieldAbsoluteDate<T> date) {
212         final Field<T> field = date.getField();
213         final T zero = field.getZero();
214 
215         // Day of year computation
216         final DateTimeComponents dtc = date.getComponents(utc);
217         final int dofyear = dtc.getDate().getDayOfYear();
218 
219         // General constants | Hydrostatic part
220         final T bh  = zero.add(0.0029);
221         final T c0h = zero.add(0.062);
222         final T c10h;
223         final T c11h;
224         final T psi;
225 
226         // Latitude and longitude of the station
227         final T latitude = point.getLatitude();
228 
229         // sin(latitude) > 0 -> northern hemisphere
230         if (FastMath.sin(latitude.getReal()) > 0) {
231             c10h = zero.add(0.001);
232             c11h = zero.add(0.005);
233             psi  = zero;
234         } else {
235             c10h = zero.add(0.002);
236             c11h = zero.add(0.007);
237             psi  = zero.getPi();
238         }
239 
240         // Compute hydrostatique coefficient c
241         // Temporal factor
242         double t0 = 28;
243         if (latitude.getReal() < 0) {
244             // southern hemisphere: t0 = 28 + an integer half of year
245             t0 += 183;
246         }
247         final T coef = psi.add(zero.getPi().multiply(2.0).multiply((dofyear - t0) / 365));
248         final T ch = c11h.divide(2.0).multiply(FastMath.cos(coef).add(1.0)).add(c10h).multiply(FastMath.cos(latitude).negate().add(1.)).add(c0h);
249 
250         // General constants | Wet part
251         final T bw = zero.add(0.00146);
252         final T cw = zero.add(0.04391);
253 
254         final T[] function = MathArrays.buildArray(field, 2);
255         function[0] = TroposphericModelUtils.mappingFunction(zero.add(coefficientsA[0]), bh, ch, elevation);
256         function[1] = TroposphericModelUtils.mappingFunction(zero.add(coefficientsA[1]), bw, cw, elevation);
257 
258         // Apply height correction
259         final T correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude(), field);
260         function[0] = function[0].add(correction);
261 
262         return function;
263     }
264 
265     /** {@inheritDoc} */
266     @Override
267     public List<ParameterDriver> getParametersDrivers() {
268         return Collections.emptyList();
269     }
270 
271 }