NiellMappingFunctionModel.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.analysis.UnivariateFunction;
  23. import org.hipparchus.analysis.interpolation.LinearInterpolator;
  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. /** The Niell Mapping Function  model for radio wavelengths.
  32.  *  This model is an empirical mapping function. It only needs the
  33.  *  values of the station latitude, height and the date for the computations.
  34.  *  <p>
  35.  *  With this model, the hydrostatic mapping function is time and latitude dependent
  36.  *  whereas the wet mapping function is only latitude dependent.
  37.  *  </p>
  38.  *
  39.  * @see A. E. Niell(1996), "Global mapping functions for the atmosphere delay of radio wavelengths,”
  40.  *      J. Geophys. Res., 101(B2), pp.  3227–3246, doi:  10.1029/95JB03048.
  41.  *
  42.  * @author Bryan Cazabonne
  43.  *
  44.  */
  45. public class NiellMappingFunctionModel implements MappingFunction {

  46.     /** Serializable UID. */
  47.     private static final long serialVersionUID = 7990311232335034375L;

  48.     /** Values for the ah average function. */
  49.     private static final double[] VALUES_FOR_AH_AVERAGE = {
  50.         1.2769934e-3, 1.2683230e-3, 1.2465397e-3, 1.2196049e-3, 1.2045996e-3
  51.     };

  52.     /** Values for the bh average function. */
  53.     private static final double[] VALUES_FOR_BH_AVERAGE = {
  54.         2.9153695e-3, 2.9152299e-3, 2.9288445e-3, 2.9022565e-3, 2.9024912e-3
  55.     };

  56.     /** Values for the ch average function. */
  57.     private static final double[] VALUES_FOR_CH_AVERAGE = {
  58.         62.610505e-3, 62.837393e-3, 63.721774e-3, 63.824265e-3, 64.258455e-3
  59.     };

  60.     /** Values for the ah amplitude function. */
  61.     private static final double[] VALUES_FOR_AH_AMPLITUDE = {
  62.         0.0, 1.2709626e-5, 2.6523662e-5, 3.4000452e-5, 4.1202191e-5
  63.     };

  64.     /** Values for the bh amplitude function. */
  65.     private static final double[] VALUES_FOR_BH_AMPLITUDE = {
  66.         0.0, 2.1414979e-5, 3.0160779e-5, 7.2562722e-5, 11.723375e-5
  67.     };

  68.     /** X values for the ch amplitude function. */
  69.     private static final double[] VALUES_FOR_CH_AMPLITUDE = {
  70.         0.0, 9.0128400e-5, 4.3497037e-5, 84.795348e-5, 170.37206e-5
  71.     };

  72.     /** Values for the aw function. */
  73.     private static final double[] VALUES_FOR_AW = {
  74.         5.8021897e-4, 5.6794847e-4, 5.8118019e-4, 5.9727542e-4, 6.1641693e-4
  75.     };

  76.     /** Values for the bw function. */
  77.     private static final double[] VALUES_FOR_BW = {
  78.         1.4275268e-3, 1.5138625e-3, 1.4572752e-3, 1.5007428e-3, 1.7599082e-3
  79.     };

  80.     /** Values for the cw function. */
  81.     private static final double[] VALUES_FOR_CW = {
  82.         4.3472961e-2, 4.6729510e-2, 4.3908931e-2, 4.4626982e-2, 5.4736038e-2
  83.     };

  84.     /** Values for the cw function. */
  85.     private static final double[] LATITUDE_VALUES = {
  86.         FastMath.toRadians(15.0), FastMath.toRadians(30.0), FastMath.toRadians(45.0), FastMath.toRadians(60.0), FastMath.toRadians(75.0),
  87.     };

  88.     /** Interpolation function for the ah (average) term. */
  89.     private final transient UnivariateFunction ahAverageFunction;

  90.     /** Interpolation function for the bh (average) term. */
  91.     private final transient UnivariateFunction bhAverageFunction;

  92.     /** Interpolation function for the ch (average) term. */
  93.     private final transient UnivariateFunction chAverageFunction;

  94.     /** Interpolation function for the ah (amplitude) term. */
  95.     private final transient UnivariateFunction ahAmplitudeFunction;

  96.     /** Interpolation function for the bh (amplitude) term. */
  97.     private final transient UnivariateFunction bhAmplitudeFunction;

  98.     /** Interpolation function for the ch (amplitude) term. */
  99.     private final transient UnivariateFunction chAmplitudeFunction;

  100.     /** Interpolation function for the aw term. */
  101.     private final transient UnivariateFunction awFunction;

  102.     /** Interpolation function for the bw term. */
  103.     private final transient UnivariateFunction bwFunction;

  104.     /** Interpolation function for the cw term. */
  105.     private final transient UnivariateFunction cwFunction;

  106.     /** Geodetic site latitude, radians.*/
  107.     private final double latitude;

  108.     /** Buils a new instance.
  109.      * @param latitude geodetic latitude of the station, in radians
  110.      */
  111.     public NiellMappingFunctionModel(final double latitude) {
  112.         // Interpolation functions for hydrostatic coefficients
  113.         this.ahAverageFunction    = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_AH_AVERAGE);
  114.         this.bhAverageFunction    = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_BH_AVERAGE);
  115.         this.chAverageFunction    = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_CH_AVERAGE);
  116.         this.ahAmplitudeFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_AH_AMPLITUDE);
  117.         this.bhAmplitudeFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_BH_AMPLITUDE);
  118.         this.chAmplitudeFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_CH_AMPLITUDE);

  119.         // Interpolation functions for wet coefficients
  120.         this.awFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_AW);
  121.         this.bwFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_BW);
  122.         this.cwFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_CW);

  123.         this.latitude = latitude;
  124.     }

  125.     @Override
  126.     public double[] mappingFactors(final double elevation, final double height,
  127.                                    final double[] parameters, final AbsoluteDate date) {
  128.         // Day of year computation
  129.         final DateTimeComponents dtc = date.getComponents(TimeScalesFactory.getUTC());
  130.         final int dofyear = dtc.getDate().getDayOfYear();

  131.         // Temporal factor
  132.         double t0 = 28;
  133.         if (latitude < 0) {
  134.             // southern hemisphere: t0 = 28 + an integer half of year
  135.             t0 += 183;
  136.         }
  137.         final double coef    = 2 * FastMath.PI * ((dofyear - t0) / 365.25);
  138.         final double cosCoef = FastMath.cos(coef);

  139.         // Compute ah, bh and ch Eq. 5
  140.         double absLatidude = FastMath.abs(latitude);
  141.         // there are no data in the model for latitudes lower than 15°
  142.         absLatidude = FastMath.max(FastMath.toRadians(15.0), absLatidude);
  143.         // there are no data in the model for latitudes greater than 75°
  144.         absLatidude = FastMath.min(FastMath.toRadians(75.0), absLatidude);
  145.         final double ah = ahAverageFunction.value(absLatidude) - ahAmplitudeFunction.value(absLatidude) * cosCoef;
  146.         final double bh = bhAverageFunction.value(absLatidude) - bhAmplitudeFunction.value(absLatidude) * cosCoef;
  147.         final double ch = chAverageFunction.value(absLatidude) - chAmplitudeFunction.value(absLatidude) * cosCoef;

  148.         final double[] function = new double[2];

  149.         // Hydrostatic mapping factor
  150.         function[0] = computeFunction(ah, bh, ch, elevation);

  151.         // Wet mapping factor
  152.         function[1] = computeFunction(awFunction.value(absLatidude), bwFunction.value(absLatidude), cwFunction.value(absLatidude), elevation);

  153.         // Apply height correction
  154.         final double correction = computeHeightCorrection(elevation, height);
  155.         function[0] = function[0] + correction;

  156.         return function;
  157.     }

  158.     @Override
  159.     public <T extends RealFieldElement<T>> T[] mappingFactors(final T elevation, final T height,
  160.                                                               final T[] parameters, final FieldAbsoluteDate<T> date) {
  161.         final Field<T> field = height.getField();
  162.         final T zero = field.getZero();

  163.         // Day of year computation
  164.         final DateTimeComponents dtc = date.getComponents(TimeScalesFactory.getUTC());
  165.         final int dofyear = dtc.getDate().getDayOfYear();

  166.         // Temporal factor
  167.         double t0 = 28;
  168.         if (latitude < 0) {
  169.             // southern hemisphere: t0 = 28 + an integer half of year
  170.             t0 += 183;
  171.         }
  172.         final T coef    = zero.add(2 * FastMath.PI * ((dofyear - t0) / 365.25));
  173.         final T cosCoef = FastMath.cos(coef);

  174.         // Compute ah, bh and ch Eq. 5
  175.         double absLatidude = FastMath.abs(latitude);
  176.         // there are no data in the model for latitudes lower than 15°
  177.         absLatidude = FastMath.max(FastMath.toRadians(15.0), absLatidude);
  178.         // there are no data in the model for latitudes greater than 75°
  179.         absLatidude = FastMath.min(FastMath.toRadians(75.0), absLatidude);
  180.         final T ah = cosCoef.multiply(ahAmplitudeFunction.value(absLatidude)).negate().add(ahAverageFunction.value(absLatidude));
  181.         final T bh = cosCoef.multiply(bhAmplitudeFunction.value(absLatidude)).negate().add(bhAverageFunction.value(absLatidude));
  182.         final T ch = cosCoef.multiply(chAmplitudeFunction.value(absLatidude)).negate().add(chAverageFunction.value(absLatidude));

  183.         final T[] function = MathArrays.buildArray(field, 2);

  184.         // Hydrostatic mapping factor
  185.         function[0] = computeFunction(ah, bh, ch, elevation);

  186.         // Wet mapping factor
  187.         function[1] = computeFunction(zero.add(awFunction.value(absLatidude)), zero.add(bwFunction.value(absLatidude)),
  188.                                       zero.add(cwFunction.value(absLatidude)), elevation);

  189.         // Apply height correction
  190.         final T correction = computeHeightCorrection(elevation, height, field);
  191.         function[0] = function[0].add(correction);

  192.         return function;
  193.     }

  194.     @Override
  195.     public List<ParameterDriver> getParametersDrivers() {
  196.         return Collections.emptyList();
  197.     }

  198.     /** Compute the mapping function related to the coefficient values and the elevation.
  199.      * @param a a coefficient
  200.      * @param b b coefficient
  201.      * @param c c coefficient
  202.      * @param elevation the elevation of the satellite, in radians.
  203.      * @return the value of the function at a given elevation
  204.      */
  205.     private double computeFunction(final double a, final double b, final double c, final double elevation) {
  206.         final double sinE = FastMath.sin(elevation);
  207.         // Numerator
  208.         final double numMP = 1 + a / (1 + b / (1 + c));
  209.         // Denominator
  210.         final double denMP = sinE + a / (sinE + b / (sinE + c));

  211.         final double felevation = numMP / denMP;

  212.         return felevation;
  213.     }

  214.     /** Compute the mapping function related to the coefficient values and the elevation.
  215.      * @param <T> type of the elements
  216.      * @param a a coefficient
  217.      * @param b b coefficient
  218.      * @param c c coefficient
  219.      * @param elevation the elevation of the satellite, in radians.
  220.      * @return the value of the function at a given elevation
  221.      */
  222.     private <T extends RealFieldElement<T>> T computeFunction(final T a, final T b, final T c, final T elevation) {
  223.         final T sinE = FastMath.sin(elevation);
  224.         // Numerator
  225.         final T numMP = a.divide(b.divide(c.add(1.0)).add(1.0)).add(1.0);
  226.         // Denominator
  227.         final T denMP = a.divide(b.divide(c.add(sinE)).add(sinE)).add(sinE);

  228.         final T felevation = numMP.divide(denMP);

  229.         return felevation;
  230.     }

  231.     /** This method computes the height correction for the hydrostatic
  232.      *  component of the mapping function (Neill, 1996).
  233.      * @param elevation the elevation of the satellite, in radians.
  234.      * @param height the height of the station in m above sea level.
  235.      * @return the height correction, in m
  236.      */
  237.     private double computeHeightCorrection(final double elevation, final double height) {
  238.         final double fixedHeight = FastMath.max(0.0, height);
  239.         final double sinE = FastMath.sin(elevation);
  240.         // Ref: Eq. 4
  241.         final double function = computeFunction(2.53e-5, 5.49e-3, 1.14e-3, elevation);
  242.         // Ref: Eq. 6
  243.         final double dmdh = (1 / sinE) - function;
  244.         // Ref: Eq. 7
  245.         final double correction = dmdh * (fixedHeight / 1000.0);
  246.         return correction;
  247.     }

  248.     /** This method computes the height correction for the hydrostatic
  249.      *  component of the mapping function (Neill, 1996).
  250.      * @param <T> type of the elements
  251.      * @param elevation the elevation of the satellite, in radians.
  252.      * @param height the height of the station in m above sea level.
  253.      * @param field field to which the elements belong
  254.      * @return the height correction, in m
  255.      */
  256.     private <T extends RealFieldElement<T>> T computeHeightCorrection(final T elevation, final T height, final Field<T> field) {
  257.         final T zero = field.getZero();
  258.         final T fixedHeight = FastMath.max(zero, height);
  259.         final T sinE = FastMath.sin(elevation);
  260.         // Ref: Eq. 4
  261.         final T function = computeFunction(zero.add(2.53e-5), zero.add(5.49e-3), zero.add(1.14e-3), elevation);
  262.         // Ref: Eq. 6
  263.         final T dmdh = sinE.reciprocal().subtract(function);
  264.         // Ref: Eq. 7
  265.         final T correction = dmdh.multiply(fixedHeight.divide(1000.0));
  266.         return correction;
  267.     }

  268. }