MendesPavlisModel.java

  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. import java.util.Collections;
  19. import java.util.List;

  20. import org.hipparchus.Field;
  21. import org.hipparchus.RealFieldElement;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathArrays;
  24. import org.orekit.time.AbsoluteDate;
  25. import org.orekit.time.FieldAbsoluteDate;
  26. import org.orekit.utils.ParameterDriver;

  27. /** The Mendes - Pavlis tropospheric delay model for optical techniques.
  28. * It is valid for a wide range of wavelengths from 0.355µm to 1.064µm (Mendes and Pavlis, 2003)
  29. *
  30. * @see Mendes, V. B., & Pavlis, E. C. (2004). High‐accuracy zenith delay prediction at
  31. *      optical wavelengths. Geophysical Research Letters, 31(14).
  32. *
  33. * @see <p>Petit, G. and Luzum, B. (eds.), IERS Conventions (2010),
  34. *      IERS Technical Note No. 36, BKG (2010)</p>
  35. *
  36. * @author Bryan Cazabonne
  37. */
  38. public class MendesPavlisModel implements DiscreteTroposphericModel {

  39.     /** Serializable UID. */
  40.     private static final long serialVersionUID = 6433588032679366305L;

  41.     /** Coefficients for the dispertion equation for the hydrostatic component [µm<sup>-2</sup>]. */
  42.     private static final double[] K_COEFFICIENTS = {
  43.         238.0185, 19990.975, 57.362, 579.55174
  44.     };

  45.     /** Coefficients for the dispertion equation for the non-hydrostatic component. */
  46.     private static final double[] W_COEFFICIENTS = {
  47.         295.235, 2.6422, -0.032380, 0.004028
  48.     };

  49.     /** Coefficients for the mapping function. */
  50.     private static final double[][] A_COEFFICIENTS = {
  51.         {12100.8e-7, 1729.5e-9, 319.1e-7, -1847.8e-11},
  52.         {30496.5e-7, 234.4e-8, -103.5e-6, -185.6e-10},
  53.         {6877.7e-5, 197.2e-7, -345.8e-5, 106.0e-9}
  54.     };

  55.     /** Carbon dioxyde content (IAG recommendations). */
  56.     private static final double C02 = 0.99995995;

  57.     /** Geodetic site latitude [rad]. */
  58.     private double latitude;

  59.     /** Laser wavelength [µm]. */
  60.     private double lambda;

  61.     /** The atmospheric pressure [hPa]. */
  62.     private double P0;

  63.     /** The temperature at the station [K]. */
  64.     private double T0;

  65.     /** Water vapor pressure at the laser site [hPa]. */
  66.     private double e0;

  67.     /** Create a new Mendes-Pavlis model for the troposphere.
  68.      * This initialisation will compute the water vapor pressure
  69.      * thanks to the values of the pressure, the temperature and the humidity
  70.      * @param t0 the temperature at the station, K
  71.      * @param p0 the atmospheric pressure at the station, hPa
  72.      * @param rh the humidity at the station, percent (50% → 0.5)
  73.      * @param latitude geodetic latitude of the station, radians
  74.      * @param lambda laser wavelength, µm
  75.      * */
  76.     public MendesPavlisModel(final double t0, final double p0, final double rh,
  77.                              final double latitude, final double lambda) {
  78.         this.P0 = p0;
  79.         this.T0 = t0;
  80.         this.e0 = getWaterVapor(rh);
  81.         this.latitude = latitude;
  82.         this.lambda   = lambda;
  83.     }

  84.     /** Create a new Mendes-Pavlis model using a standard atmosphere model.
  85.     *
  86.     * <ul>
  87.     * <li>temperature: 18 degree Celsius
  88.     * <li>pressure: 1013.25 hPa
  89.     * <li>humidity: 50%
  90.     * </ul>
  91.     *
  92.     * @param latitude site latitude, radians
  93.     * @param lambda laser wavelength, µm
  94.     *
  95.     * @return a Mendes-Pavlis model with standard environmental values
  96.     */
  97.     public static MendesPavlisModel getStandardModel(final double latitude, final double lambda) {
  98.         return new MendesPavlisModel(273.15 + 18, 1013.25, 0.5, latitude, lambda);
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public double pathDelay(final double elevation, final double height,
  103.                             final double[] parameters, final AbsoluteDate date) {
  104.         // Zenith delay
  105.         final double[] zenithDelay = computeZenithDelay(height, parameters, date);
  106.         // Mapping function
  107.         final double[] mappingFunction = mappingFactors(elevation, height, parameters, date);
  108.         // Tropospheric path delay
  109.         return zenithDelay[0] * mappingFunction[0] + zenithDelay[1] * mappingFunction[1];
  110.     }

  111.     /** {@inheritDoc} */
  112.     @Override
  113.     public <T extends RealFieldElement<T>> T pathDelay(final T elevation, final T height,
  114.                                                        final T[] parameters, final FieldAbsoluteDate<T> date) {
  115.         // Zenith delay
  116.         final T[] delays = computeZenithDelay(height, parameters, date);
  117.         // Mapping function
  118.         final T[] mappingFunction = mappingFactors(elevation, height, parameters, date);
  119.         // Tropospheric path delay
  120.         return delays[0].multiply(mappingFunction[0]).add(delays[1].multiply(mappingFunction[1]));
  121.     }

  122.     /** {@inheritDoc} */
  123.     @Override
  124.     public double[] computeZenithDelay(final double height, final double[] parameters, final AbsoluteDate date) {
  125.         final double fsite   = getSiteFunctionValue(height);

  126.         // Array for zenith delay
  127.         final double[] delay = new double[2];

  128.         // Dispertion Equation for the Hydrostatic component
  129.         final double sigma  = 1 / lambda;
  130.         final double sigma2 = sigma * sigma;
  131.         final double coef1  = K_COEFFICIENTS[0] + sigma2;
  132.         final double coef2  = K_COEFFICIENTS[0] - sigma2;
  133.         final double coef3  = K_COEFFICIENTS[2] + sigma2;
  134.         final double coef4  = K_COEFFICIENTS[2] - sigma2;

  135.         final double frac1 = coef1 / (coef2 * coef2);
  136.         final double frac2 = coef3 / (coef4 * coef4);

  137.         final double fLambdaH = 0.01 * (K_COEFFICIENTS[1] * frac1 + K_COEFFICIENTS[3] * frac2) * C02;

  138.         // Zenith delay for the hydrostatic component
  139.         delay[0] = 0.002416579 * (fLambdaH / fsite) * P0;

  140.         // Dispertion Equation for the Non-Hydrostatic component
  141.         final double sigma4 = sigma2 * sigma2;
  142.         final double sigma6 = sigma4 * sigma2;
  143.         final double w1s2  = 3 * W_COEFFICIENTS[1] * sigma2;
  144.         final double w2s4  = 5 * W_COEFFICIENTS[2] * sigma4;
  145.         final double w3s6  = 7 * W_COEFFICIENTS[3] * sigma6;

  146.         final double fLambdaNH = 0.003101 * (W_COEFFICIENTS[0] + w1s2 + w2s4 + w3s6);

  147.         // Zenith delay for the non-hydrostatic component
  148.         delay[1] = 0.0001 * (5.316 * fLambdaNH - 3.759 * fLambdaH) * (e0 / fsite);

  149.         return delay;
  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     public <T extends RealFieldElement<T>> T[] computeZenithDelay(final T height, final T[] parameters,
  154.                                                                   final FieldAbsoluteDate<T> date) {
  155.         final Field<T> field = height.getField();
  156.         final T zero = field.getZero();

  157.         final T fsite   = getSiteFunctionValue(height);

  158.         // Array for zenith delay
  159.         final T[] delay = MathArrays.buildArray(field, 2);

  160.         // Dispertion Equation for the Hydrostatic component
  161.         final T sigma  = zero.add(1 / lambda);
  162.         final T sigma2 = sigma.multiply(sigma);
  163.         final T coef1  = sigma2.add(K_COEFFICIENTS[0]);
  164.         final T coef2  = sigma2.negate().add(K_COEFFICIENTS[0]);
  165.         final T coef3  = sigma2.add(K_COEFFICIENTS[2]);
  166.         final T coef4  = sigma2.negate().add(K_COEFFICIENTS[2]);

  167.         final T frac1 = coef1.divide(coef2.multiply(coef2));
  168.         final T frac2 = coef3.divide(coef4.multiply(coef4));

  169.         final T fLambdaH = frac1.multiply(K_COEFFICIENTS[1]).add(frac2.multiply(K_COEFFICIENTS[3])).multiply(0.01 * C02);

  170.         // Zenith delay for the hydrostatic component
  171.         delay[0] =  fLambdaH.divide(fsite).multiply(P0).multiply(0.002416579);

  172.         // Dispertion Equation for the Non-Hydrostatic component
  173.         final T sigma4 = sigma2.multiply(sigma2);
  174.         final T sigma6 = sigma4.multiply(sigma2);
  175.         final T w1s2   = sigma2.multiply(3 * W_COEFFICIENTS[1]);
  176.         final T w2s4   = sigma4.multiply(5 * W_COEFFICIENTS[2]);
  177.         final T w3s6   = sigma6.multiply(7 * W_COEFFICIENTS[3]);

  178.         final T fLambdaNH = w1s2.add(w2s4).add(w3s6).add(W_COEFFICIENTS[0]).multiply(0.003101);

  179.         // Zenith delay for the non-hydrostatic component
  180.         delay[1] = fLambdaNH.multiply(5.316).subtract(fLambdaH.multiply(3.759)).multiply(fsite.divide(e0).reciprocal()).multiply(0.0001);

  181.         return delay;
  182.     }

  183.     /** With the Mendes Pavlis tropospheric model, the mapping
  184.      * function is not split into hydrostatic and wet component.
  185.      * <p>
  186.      * Therefore, the two components of the resulting array are
  187.      * equals.
  188.      * <ul>
  189.      * <li>double[0] = m(e) → total mapping function
  190.      * <li>double[1] = m(e) → total mapping function
  191.      * </ul>
  192.      * </p><p>
  193.      * The total delay will thus be computed as this:
  194.      * <pre>
  195.      * δ = D<sub>hz</sub> * m(e) + D<sub>wz</sub> * m(e)
  196.      * </pre>
  197.      * <pre>
  198.      * δ = (D<sub>hz</sub> + D<sub>wz</sub>) * m(e) = δ<sub>z</sub> * m(e)
  199.      * </pre>
  200.      * </p>
  201.      * */
  202.     @Override
  203.     public double[] mappingFactors(final double elevation, final double height,
  204.                                    final double[] parameters, final AbsoluteDate date) {
  205.         final double sinE = FastMath.sin(elevation);

  206.         final double T2degree = T0 - 273.15;

  207.         // Mapping function coefficients
  208.         final double a1 = computeMFCoeffient(A_COEFFICIENTS[0][0], A_COEFFICIENTS[0][1],
  209.                                              A_COEFFICIENTS[0][2], A_COEFFICIENTS[0][3],
  210.                                              T2degree, height);
  211.         final double a2 = computeMFCoeffient(A_COEFFICIENTS[1][0], A_COEFFICIENTS[1][1],
  212.                                              A_COEFFICIENTS[1][2], A_COEFFICIENTS[1][3],
  213.                                              T2degree, height);
  214.         final double a3 = computeMFCoeffient(A_COEFFICIENTS[2][0], A_COEFFICIENTS[2][1],
  215.                                              A_COEFFICIENTS[2][2], A_COEFFICIENTS[2][3],
  216.                                              T2degree, height);

  217.         // Numerator
  218.         final double numMP = 1 + a1 / (1 + a2 / (1 + a3));
  219.         // Denominator
  220.         final double denMP = sinE + a1 / (sinE + a2 / (sinE + a3));

  221.         final double factor = numMP / denMP;

  222.         return new double[] {
  223.             factor,
  224.             factor
  225.         };
  226.     }

  227.     /** {@inheritDoc} */
  228.     @Override
  229.     public <T extends RealFieldElement<T>> T[] mappingFactors(final T elevation, final T height,
  230.                                                               final T[] parameters, final FieldAbsoluteDate<T> date) {
  231.         final Field<T> field = date.getField();

  232.         final T sinE = FastMath.sin(elevation);

  233.         final double T2degree = T0 - 273.15;

  234.         // Mapping function coefficients
  235.         final T a1 = computeMFCoeffient(A_COEFFICIENTS[0][0], A_COEFFICIENTS[0][1],
  236.                                         A_COEFFICIENTS[0][2], A_COEFFICIENTS[0][3],
  237.                                         T2degree, height);
  238.         final T a2 = computeMFCoeffient(A_COEFFICIENTS[1][0], A_COEFFICIENTS[1][1],
  239.                                         A_COEFFICIENTS[1][2], A_COEFFICIENTS[1][3],
  240.                                         T2degree, height);
  241.         final T a3 = computeMFCoeffient(A_COEFFICIENTS[2][0], A_COEFFICIENTS[2][1],
  242.                                         A_COEFFICIENTS[2][2], A_COEFFICIENTS[2][3],
  243.                                         T2degree, height);

  244.         // Numerator
  245.         final T numMP = a1.divide(a2.divide(a3.add(1.0)).add(1.0)).add(1.0);
  246.         // Denominator
  247.         final T denMP = a1.divide(a2.divide(a3.add(sinE)).add(sinE)).add(sinE);

  248.         final T factor = numMP.divide(denMP);

  249.         final T[] mapping = MathArrays.buildArray(field, 2);
  250.         mapping[0] = factor;
  251.         mapping[1] = factor;

  252.         return mapping;
  253.     }

  254.     /** {@inheritDoc} */
  255.     @Override
  256.     public List<ParameterDriver> getParametersDrivers() {
  257.         return Collections.emptyList();
  258.     }

  259.     /** Get the laser frequency parameter f(lambda).
  260.     *
  261.     * @param height height above the geoid, m
  262.     * @return the laser frequency parameter f(lambda).
  263.     */
  264.     private double getSiteFunctionValue(final double height) {
  265.         return 1. - 0.00266 * FastMath.cos(2 * latitude) - 0.00000028 * height;
  266.     }

  267.     /** Get the laser frequency parameter f(lambda).
  268.     *
  269.     * @param <T> type of the elements
  270.     * @param height height above the geoid, m
  271.     * @return the laser frequency parameter f(lambda).
  272.     */
  273.     private <T extends RealFieldElement<T>> T getSiteFunctionValue(final T height) {
  274.         return height.multiply(0.00000028).negate().add(1. - 0.00266 * FastMath.cos(2 * latitude));
  275.     }

  276.     /** Compute the coefficients of the Mapping Function.
  277.     *
  278.     * @param T the temperature at the station site, °C
  279.     * @param a0 first coefficient
  280.     * @param a1 second coefficient
  281.     * @param a2 third coefficient
  282.     * @param a3 fourth coefficient
  283.     * @param height the height of the station in m above sea level
  284.     * @return the value of the coefficient
  285.     */
  286.     private double computeMFCoeffient(final double a0, final double a1, final double a2, final double a3,
  287.                                       final double T, final double height) {
  288.         return a0 + a1 * T + a2 * FastMath.cos(latitude) + a3 * height;
  289.     }

  290.    /** Compute the coefficients of the Mapping Function.
  291.    *
  292.    * @param <T> type of the elements
  293.    * @param temp the temperature at the station site, °C
  294.    * @param a0 first coefficient
  295.    * @param a1 second coefficient
  296.    * @param a2 third coefficient
  297.    * @param a3 fourth coefficient
  298.    * @param height the height of the station in m above sea level
  299.    * @return the value of the coefficient
  300.    */
  301.     private <T extends RealFieldElement<T>> T computeMFCoeffient(final double a0, final double a1, final double a2, final double a3,
  302.                                                                  final double temp, final T height) {
  303.         return height.multiply(a3).add(a0 + a1 * temp + a2 * FastMath.cos(latitude));
  304.     }

  305.     /** Get the water vapor.
  306.      * The water vapor model is the one of Giacomo and Davis as indicated in IERS TN 32, chap. 9.
  307.      *
  308.      * See: Giacomo, P., Equation for the dertermination of the density of moist air, Metrologia, V. 18, 1982
  309.      *
  310.      * @param rh relative humidity, in percent (50% → 0.5).
  311.      * @return the water vapor, in mbar (1 mbar = 1 hPa).
  312.      */
  313.     private double getWaterVapor(final double rh) {

  314.         // saturation water vapor, equation (3) of reference paper, in mbar
  315.         // with amended 1991 values (see reference paper)
  316.         final double es = 0.01 * FastMath.exp((1.2378847 * 1e-5) * T0 * T0 -
  317.                                               (1.9121316 * 1e-2) * T0 +
  318.                                               33.93711047 -
  319.                                               (6.3431645 * 1e3) * 1. / T0);

  320.         // enhancement factor, equation (4) of reference paper
  321.         final double fw = 1.00062 + (3.14 * 1e-6) * P0 + (5.6 * 1e-7) * FastMath.pow(T0 - 273.15, 2);

  322.         final double e = rh * fw * es;
  323.         return e;
  324.     }
  325. }