NiellMappingFunctionModel.java

  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. import org.hipparchus.Field;
  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.analysis.UnivariateFunction;
  21. import org.hipparchus.analysis.interpolation.LinearInterpolator;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathArrays;
  24. import org.orekit.annotation.DefaultDataContext;
  25. import org.orekit.bodies.FieldGeodeticPoint;
  26. import org.orekit.bodies.GeodeticPoint;
  27. import org.orekit.data.DataContext;
  28. import org.orekit.time.AbsoluteDate;
  29. import org.orekit.time.FieldAbsoluteDate;
  30. import org.orekit.time.TimeScale;
  31. import org.orekit.utils.FieldTrackingCoordinates;
  32. import org.orekit.utils.TrackingCoordinates;

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

  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 UnivariateFunction ahAverageFunction;

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

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

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

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

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

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

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

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

  106.     /** UTC time scale. */
  107.     private final TimeScale utc;

  108.     /** Builds a new instance.
  109.      *
  110.      * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
  111.      *
  112.      * @see #NiellMappingFunctionModel(TimeScale)
  113.      */
  114.     @DefaultDataContext
  115.     public NiellMappingFunctionModel() {
  116.         this(DataContext.getDefault().getTimeScales().getUTC());
  117.     }

  118.     /** Builds a new instance.
  119.      * @param utc UTC time scale.
  120.      * @since 10.1
  121.      */
  122.     public NiellMappingFunctionModel(final TimeScale utc) {
  123.         this.utc = utc;
  124.         // Interpolation functions for hydrostatic coefficients
  125.         this.ahAverageFunction    = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_AH_AVERAGE);
  126.         this.bhAverageFunction    = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_BH_AVERAGE);
  127.         this.chAverageFunction    = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_CH_AVERAGE);
  128.         this.ahAmplitudeFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_AH_AMPLITUDE);
  129.         this.bhAmplitudeFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_BH_AMPLITUDE);
  130.         this.chAmplitudeFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_CH_AMPLITUDE);

  131.         // Interpolation functions for wet coefficients
  132.         this.awFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_AW);
  133.         this.bwFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_BW);
  134.         this.cwFunction  = new LinearInterpolator().interpolate(LATITUDE_VALUES, VALUES_FOR_CW);
  135.     }

  136.     /** {@inheritDoc} */
  137.     @Override
  138.     public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point,
  139.                                    final AbsoluteDate date) {

  140.         // Temporal factor
  141.         double t0 = 28;
  142.         if (point.getLatitude() < 0) {
  143.             // southern hemisphere: t0 = 28 + an integer half of year
  144.             t0 += 183;
  145.         }
  146.         final double coef    = 2 * FastMath.PI * ((date.getDayOfYear(utc) - t0) / 365.25);
  147.         final double cosCoef = FastMath.cos(coef);

  148.         // Compute ah, bh and ch Eq. 5
  149.         double absLatidude = FastMath.abs(point.getLatitude());
  150.         // there are no data in the model for latitudes lower than 15°
  151.         absLatidude = FastMath.max(FastMath.toRadians(15.0), absLatidude);
  152.         // there are no data in the model for latitudes greater than 75°
  153.         absLatidude = FastMath.min(FastMath.toRadians(75.0), absLatidude);
  154.         final double ah = ahAverageFunction.value(absLatidude) - ahAmplitudeFunction.value(absLatidude) * cosCoef;
  155.         final double bh = bhAverageFunction.value(absLatidude) - bhAmplitudeFunction.value(absLatidude) * cosCoef;
  156.         final double ch = chAverageFunction.value(absLatidude) - chAmplitudeFunction.value(absLatidude) * cosCoef;

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

  158.         // Hydrostatic mapping factor
  159.         function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, trackingCoordinates.getElevation());

  160.         // Wet mapping factor
  161.         function[1] = TroposphericModelUtils.mappingFunction(awFunction.value(absLatidude),
  162.                                                              bwFunction.value(absLatidude),
  163.                                                              cwFunction.value(absLatidude),
  164.                                                              trackingCoordinates.getElevation());

  165.         // Apply height correction
  166.         final double correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(),
  167.                                                                                  point.getAltitude());
  168.         function[0] = function[0] + correction;

  169.         return function;
  170.     }

  171.     /** {@inheritDoc} */
  172.     @Override
  173.     public <T extends CalculusFieldElement<T>> T[] mappingFactors(final FieldTrackingCoordinates<T> trackingCoordinates,
  174.                                                                   final FieldGeodeticPoint<T> point,
  175.                                                                   final FieldAbsoluteDate<T> date) {
  176.         final Field<T> field = date.getField();
  177.         final T zero = field.getZero();

  178.         // Temporal factor
  179.         double t0 = 28;
  180.         if (point.getLatitude().getReal() < 0) {
  181.             // southern hemisphere: t0 = 28 + an integer half of year
  182.             t0 += 183;
  183.         }
  184.         final T coef    = zero.getPi().multiply(2.0).multiply((date.getDayOfYear(utc).subtract(t0)).divide(365.25));
  185.         final T cosCoef = FastMath.cos(coef);

  186.         // Compute ah, bh and ch Eq. 5
  187.         double absLatidude = FastMath.abs(point.getLatitude().getReal());
  188.         // there are no data in the model for latitudes lower than 15°
  189.         absLatidude = FastMath.max(FastMath.toRadians(15.0), absLatidude);
  190.         // there are no data in the model for latitudes greater than 75°
  191.         absLatidude = FastMath.min(FastMath.toRadians(75.0), absLatidude);
  192.         final T ah = cosCoef.multiply(ahAmplitudeFunction.value(absLatidude)).negate().add(ahAverageFunction.value(absLatidude));
  193.         final T bh = cosCoef.multiply(bhAmplitudeFunction.value(absLatidude)).negate().add(bhAverageFunction.value(absLatidude));
  194.         final T ch = cosCoef.multiply(chAmplitudeFunction.value(absLatidude)).negate().add(chAverageFunction.value(absLatidude));

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

  196.         // Hydrostatic mapping factor
  197.         function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch,
  198.                                                              trackingCoordinates.getElevation());

  199.         // Wet mapping factor
  200.         function[1] = TroposphericModelUtils.mappingFunction(zero.newInstance(awFunction.value(absLatidude)), zero.newInstance(bwFunction.value(absLatidude)),
  201.                                                              zero.newInstance(cwFunction.value(absLatidude)), trackingCoordinates.getElevation());

  202.         // Apply height correction
  203.         final T correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(),
  204.                                                                             point.getAltitude(),
  205.                                                                             field);
  206.         function[0] = function[0].add(correction);

  207.         return function;
  208.     }

  209. }