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.iturp834;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.analysis.interpolation.GridAxis;
21  import org.hipparchus.util.FastMath;
22  import org.orekit.bodies.FieldGeodeticPoint;
23  import org.orekit.bodies.GeodeticPoint;
24  import org.orekit.utils.units.Unit;
25  
26  /** Utility class for ITU-R P.834 gravity parameters.
27   * <p>
28   * This class implements the gravity parts of the model,
29   * i.e. equations 27g and 27j in section 6 of the recommendation.
30   * </p>
31   * @see ITURP834PathDelay
32   * @see ITURP834MappingFunction
33   * @see ITURP834WeatherParametersProvider
34   * @author Luc Maisonobe
35   * @see <a href="https://www.itu.int/rec/R-REC-P.834/en">P.834 : Effects of tropospheric refraction on radiowave propagation</a>
36   * @since 13.0
37   */
38  class Gravity {
39  
40      /** Name of height reference level. */
41      public static final String AVERAGE_HEIGHT_REFERENCE_LEVEL_NAME = "hreflev.dat";
42  
43      /** Unit per km. */
44      private static final Unit UNIT_PER_KM = Unit.parse("km⁻¹");
45  
46      /** Gravity factor for equation 27g. */
47      private static final double G_27G = 9.806;
48  
49      /** Gravity latitude correction factor for equation 27g. */
50      private static final double GL_27G = 0.002637;
51  
52      /** Gravity altitude correction factor for equation 27g. */
53      private static final double GH_27G = UNIT_PER_KM.toSI(0.00031);
54  
55      /** Gravity factor for equation 27j. */
56      private static final double G_27J = 9.784;
57  
58      /** Gravity latitude correction factor for equation 27j. */
59      private static final double GL_27J = 0.00266;
60  
61      /** Gravity altitude correction factor for equation 27j. */
62      private static final double GH_27J = UNIT_PER_KM.toSI(0.00028);
63  
64      /** Gravity at Earth surface. */
65      private static final ConstantGrid GS =
66          new ConstantGrid(Unit.METRE, AVERAGE_HEIGHT_REFERENCE_LEVEL_NAME).
67                  apply((lat, lon, h) -> G_27G * (1 - GL_27G * FastMath.cos(2 * lat) - GH_27G * h));
68  
69      /** Private constructor for a utility class.
70       */
71      private Gravity() {
72          // nothing to do
73      }
74  
75      /** Get gravity at surface.
76       * @param location point location on Earth
77       * @return gravity model over one cell
78       */
79      public static GridCell getGravityAtSurface(final GeodeticPoint location) {
80          return GS.getCell(location, 0.0);
81      }
82  
83      /** Get gravity at surface.
84       * @param <T> type of the field elements
85       * @param location point location on Earth
86       * @return gravity model over one cell
87       */
88      public static <T extends CalculusFieldElement<T>> FieldGridCell<T> getGravityAtSurface(final FieldGeodeticPoint<T> location) {
89          return GS.getCell(location, null);
90      }
91  
92      /** Get gravity at point altitude.
93       * @param location point location on Earth
94       * @return gravity model over one cell
95       */
96      public static GridCell getGravityAtAltitude(final GeodeticPoint location) {
97          final GridAxis latitudeAxis  = GS.getLatitudeAxis();
98          final int      southIndex    = latitudeAxis.interpolationIndex(location.getLatitude());
99          final double   northLatitude = latitudeAxis.node(southIndex + 1);
100         final double   southLatitude = latitudeAxis.node(southIndex);
101         final GridAxis longitudeAxis = GS.getLongitudeAxis();
102         final int      westIndex     = longitudeAxis.interpolationIndex(location.getLongitude());
103         final double   westLongitude = longitudeAxis.node(westIndex);
104         final double   mga           = -GH_27J * location.getAltitude();
105         final double   gNorth        = (mga + 1 - GL_27J * FastMath.cos(2 * northLatitude)) * G_27J;
106         final double   gSouth        = (mga + 1 - GL_27J * FastMath.cos(2 * southLatitude)) * G_27J;
107         return new GridCell(location.getLatitude()  - southLatitude,
108                             location.getLongitude() - westLongitude,
109                             GS.getSizeLat(), GS.getSizeLon(),
110                             gNorth, gSouth, gSouth, gNorth);
111     }
112 
113     /** Get gravity at point altitude.
114      * @param <T> type of the field elements
115      * @param location point location on Earth
116      * @return gravity model over one cell
117      */
118     public static <T extends CalculusFieldElement<T>> FieldGridCell<T> getGravityAtAltitude(
119         final FieldGeodeticPoint<T> location) {
120         final GridAxis latitudeAxis  = GS.getLatitudeAxis();
121         final int      southIndex    = latitudeAxis.interpolationIndex(location.getLatitude().getReal());
122         final double   northLatitude = latitudeAxis.node(southIndex + 1);
123         final double   southLatitude = latitudeAxis.node(southIndex);
124         final GridAxis longitudeAxis = GS.getLongitudeAxis();
125         final int      westIndex     = longitudeAxis.interpolationIndex(location.getLongitude().getReal());
126         final double   westLongitude = longitudeAxis.node(westIndex);
127         final T        mga           = location.getAltitude().multiply(GH_27J).negate();
128         final T        gNorth        = mga.add(1 - GL_27J * FastMath.cos(2 * northLatitude)).multiply(G_27J);
129         final T        gSouth        = mga.add(1 - GL_27J * FastMath.cos(2 * southLatitude)).multiply(G_27J);
130         return new FieldGridCell<>(location.getLatitude().subtract(southLatitude),
131                                    location.getLongitude().subtract(westLongitude),
132                                    GS.getSizeLat(), GS.getSizeLon(), gNorth,
133                                    gSouth, gSouth, gNorth);
134     }
135 
136 }