1   /* Copyright 2022-2025 Thales Alenia Space
2    * Licensed to CS Communication & Systèmes (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.CalculusFieldElement;
23  import org.orekit.bodies.FieldGeodeticPoint;
24  import org.orekit.bodies.GeodeticPoint;
25  import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity;
26  import org.orekit.models.earth.weather.PressureTemperatureHumidity;
27  import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.FieldAbsoluteDate;
30  import org.orekit.utils.Constants;
31  import org.orekit.utils.FieldTrackingCoordinates;
32  import org.orekit.utils.ParameterDriver;
33  import org.orekit.utils.TrackingCoordinates;
34  
35  /** The Askne Nordius model.
36   * <p>
37   * The hydrostatic part is equivalent to Saastamoinen, whereas the wet part takes
38   * into account {@link PressureTemperatureHumidity#getTm() mean temperature weighted
39   * with water vapor pressure} and {@link PressureTemperatureHumidity#getLambda() water
40   * vapor decrease factor}.
41   * </p>
42   * @author Luc Maisonobe
43   * @see "J. Askne and H. Nordius, Estimation of tropospheric delay for microwaves
44   *      from surface weather data, Radio Science, volume 22, number 3, pages 379-386,
45   *      May-June 1987"
46   * @see "Landskron D (2017) Modeling tropospheric delays for space geodetic
47   *      techniques. Dissertation, Department of Geodesy and Geoinformation, TU Wien, Supervisor: J. Böhm.
48   *      http://repositum.tuwien.ac.at/urn:nbn:at:at-ubtuw:1-100249"
49   * @since 12.1
50   */
51  public class AskneNordiusModel implements TroposphericModel {
52  
53      /** Lowest acceptable elevation angle [rad]. */
54      public static final double LOW_ELEVATION_THRESHOLD = 0.05;
55  
56      /** Base delay coefficient (from Saastamoninen model). */
57      private static final double L0 = 2.2768e-5;
58  
59      /** Askne-Nordius coefficient k'₂. */
60      private static final double K_PRIME_2 = 16.5203;
61  
62      /** Askne-Nordius coefficient k₃. */
63      private static final double K_3 = 377600;
64  
65      /** Gas constant for dry components. */
66      private static final double RD = 287.0464;
67  
68      /** Unit consversion factor. */
69      private static final double FACTOR = 1.0e-6;
70  
71      /** Mapping function. */
72      private final TroposphereMappingFunction mappingFunction;
73  
74      /** Provider for pressure, temperature and humidity.
75       * @since 13.0
76       */
77      private final PressureTemperatureHumidityProvider pthProvider;
78  
79      /** Create a new Askne Nordius model.
80       * @param mappingFunction mapping function
81       * @param pthProvider provider for pressure, temperature and humidity
82       */
83      public AskneNordiusModel(final TroposphereMappingFunction mappingFunction,
84                               final PressureTemperatureHumidityProvider pthProvider) {
85          this.mappingFunction = mappingFunction;
86          this.pthProvider     = pthProvider;
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point,
92                                         final double[] parameters, final AbsoluteDate date) {
93  
94          final double[] mf = mappingFunction.mappingFactors(trackingCoordinates, point, date);
95  
96          // compute weather parameters
97          final PressureTemperatureHumidity weather = pthProvider.getWeatherParameters(point, date);
98  
99          // compute the path delay
100         final double zh     = L0 * weather.getPressure();
101         final double zw     = FACTOR * (K_PRIME_2 + K_3 / weather.getTm()) *
102                               RD * weather.getWaterVaporPressure() /
103                               (Constants.G0_STANDARD_GRAVITY * (weather.getLambda() + 1.0));
104         final double sh     = zh * mf[0];
105         final double sw     = zw * mf[1];
106         return new TroposphericDelay(zh, zw, sh, sw);
107 
108     }
109 
110     /** {@inheritDoc} */
111     @Override
112     public <T extends CalculusFieldElement<T>> FieldTroposphericDelay<T> pathDelay(final FieldTrackingCoordinates<T> trackingCoordinates,
113                                                                                    final FieldGeodeticPoint<T> point,
114                                                                                    final T[] parameters, final FieldAbsoluteDate<T> date) {
115 
116         final T[] mf = mappingFunction.mappingFactors(trackingCoordinates, point, date);
117 
118         // compute weather parameters
119         final FieldPressureTemperatureHumidity<T> weather = pthProvider.getWeatherParameters(point, date);
120 
121         // compute the path delay
122         final T zh     = weather.getPressure().multiply(L0);
123         final T zw     = weather.getTm().reciprocal().multiply(K_3).add(K_PRIME_2).
124                          multiply(weather.getWaterVaporPressure().multiply(RD)).
125                          divide(weather.getLambda().add(1.0).multiply(Constants.G0_STANDARD_GRAVITY)).
126                          multiply(FACTOR);
127         final T sh     = zh.multiply(mf[0]);
128         final T sw     = zw.multiply(mf[1]);
129         return new FieldTroposphericDelay<>(zh, zw, sh, sw);
130 
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public List<ParameterDriver> getParametersDrivers() {
136         return Collections.emptyList();
137     }
138 
139 }