1   /* Copyright 2002-2025 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 org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.MathArrays;
23  import org.hipparchus.util.MathUtils;
24  import org.orekit.bodies.FieldGeodeticPoint;
25  import org.orekit.bodies.GeodeticPoint;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.time.FieldAbsoluteDate;
28  import org.orekit.time.TimeScale;
29  import org.orekit.utils.FieldTrackingCoordinates;
30  import org.orekit.utils.TrackingCoordinates;
31  
32  /** The Vienna 1 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   * This version considered the height correction for the hydrostatic part
36   * developed by Niell, 1996.
37   *
38   * @see "Boehm, J., Werl, B., and Schuh, H., (2006),
39   *       Troposhere mapping functions for GPS and very long baseline
40   *       interferometry from European Centre for Medium-Range Weather
41   *       Forecasts operational analysis data, J. Geophy. Res., Vol. 111,
42   *       B02406, doi:10.1029/2005JB003629"
43   * @since 12.1
44   * @author Bryan Cazabonne
45   * @author Luc Maisonobe
46   */
47  public class ViennaOne extends AbstractVienna {
48  
49      /** Build a new instance.
50       * @param aProvider provider for a<sub>h</sub> and a<sub>w</sub> coefficients
51       * @param gProvider provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients}
52       * @param zenithDelayProvider provider for zenith delays
53       * @param utc                 UTC time scale
54       */
55      public ViennaOne(final ViennaAProvider aProvider,
56                       final AzimuthalGradientProvider gProvider,
57                       final TroposphericModel zenithDelayProvider,
58                       final TimeScale utc) {
59          super(aProvider, gProvider, zenithDelayProvider, utc);
60      }
61  
62      /** {@inheritDoc} */
63      @Override
64      public double[] mappingFactors(final TrackingCoordinates trackingCoordinates,
65                                     final GeodeticPoint point,
66                                     final AbsoluteDate date) {
67  
68          // a coefficients
69          final ViennaACoefficients a = getAProvider().getA(point, date);
70  
71          // Day of year computation
72          final double dofyear = getDayOfYear(date);
73  
74          // General constants | Hydrostatic part
75          final double bh  = 0.0029;
76          final double c0h = 0.062;
77          final double c10h;
78          final double c11h;
79          final double psi;
80  
81          // Latitude of the station
82          final double latitude = point.getLatitude();
83  
84          // sin(latitude) > 0 -> northern hemisphere
85          if (FastMath.sin(latitude) > 0) {
86              c10h = 0.001;
87              c11h = 0.005;
88              psi  = 0;
89          } else {
90              c10h = 0.002;
91              c11h = 0.007;
92              psi  = FastMath.PI;
93          }
94  
95          // Temporal factor
96          double t0 = 28;
97          if (latitude < 0) {
98              // southern hemisphere: t0 = 28 + an integer half of year
99              t0 += 183;
100         }
101         // Compute hydrostatique coefficient c
102         final double coef = psi + ((dofyear - t0) / 365) * MathUtils.TWO_PI;
103         final double ch = c0h + ((FastMath.cos(coef) + 1) * (c11h / 2) + c10h) * (1 - FastMath.cos(latitude));
104 
105         // General constants | Wet part
106         final double bw = 0.00146;
107         final double cw = 0.04391;
108 
109         final double[] function = new double[2];
110         function[0] = TroposphericModelUtils.mappingFunction(a.getAh(), bh, ch,
111                                                              trackingCoordinates.getElevation());
112         function[1] = TroposphericModelUtils.mappingFunction(a.getAw(), bw, cw,
113                                                              trackingCoordinates.getElevation());
114 
115         // Apply height correction
116         final double correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(),
117                                                                                  point.getAltitude());
118         function[0] = function[0] + correction;
119 
120         return function;
121     }
122 
123     /** {@inheritDoc} */
124     @Override
125     public <T extends CalculusFieldElement<T>> T[] mappingFactors(final FieldTrackingCoordinates<T> trackingCoordinates,
126                                                                   final FieldGeodeticPoint<T> point,
127                                                                   final FieldAbsoluteDate<T> date) {
128 
129         final Field<T> field = date.getField();
130         final T zero = field.getZero();
131 
132         // a coefficients
133         final FieldViennaACoefficients<T> a = getAProvider().getA(point, date);
134 
135         // Day of year computation
136         final T dofyear = getDayOfYear(date);
137 
138         // General constants | Hydrostatic part
139         final T bh  = zero.newInstance(0.0029);
140         final T c0h = zero.newInstance(0.062);
141         final T c10h;
142         final T c11h;
143         final T psi;
144 
145         // Latitude and longitude of the station
146         final T latitude = point.getLatitude();
147 
148         // sin(latitude) > 0 -> northern hemisphere
149         if (FastMath.sin(latitude.getReal()) > 0) {
150             c10h = zero.newInstance(0.001);
151             c11h = zero.newInstance(0.005);
152             psi  = zero;
153         } else {
154             c10h = zero.newInstance(0.002);
155             c11h = zero.newInstance(0.007);
156             psi  = zero.getPi();
157         }
158 
159         // Compute hydrostatique coefficient c
160         // Temporal factor
161         double t0 = 28;
162         if (latitude.getReal() < 0) {
163             // southern hemisphere: t0 = 28 + an integer half of year
164             t0 += 183;
165         }
166         final T coef = psi.add(dofyear.subtract(t0).divide(365).multiply(MathUtils.TWO_PI));
167         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);
168 
169         // General constants | Wet part
170         final T bw = zero.newInstance(0.00146);
171         final T cw = zero.newInstance(0.04391);
172 
173         final T[] function = MathArrays.buildArray(field, 2);
174         function[0] = TroposphericModelUtils.mappingFunction(a.getAh(), bh, ch,
175                                                              trackingCoordinates.getElevation());
176         function[1] = TroposphericModelUtils.mappingFunction(a.getAw(), bw, cw,
177                                                              trackingCoordinates.getElevation());
178 
179         // Apply height correction
180         final T correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(),
181                                                                             point.getAltitude(),
182                                                                             field);
183         function[0] = function[0].add(correction);
184 
185         return function;
186     }
187 
188 }