Gravity.java

/* Copyright 2002-2024 Thales Alenia Space
 * Licensed to CS Communication & Systèmes (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.models.earth.troposphere.iturp834;

import org.hipparchus.CalculusFieldElement;
import org.hipparchus.analysis.interpolation.GridAxis;
import org.hipparchus.util.FastMath;
import org.orekit.bodies.FieldGeodeticPoint;
import org.orekit.bodies.GeodeticPoint;
import org.orekit.utils.units.Unit;

/** Utility class for ITU-R P.834 gravity parameters.
 * <p>
 * This class implements the gravity parts of the model,
 * i.e. equations 27g and 27j in section 6 of the recommendation.
 * </p>
 * @see ITURP834PathDelay
 * @see ITURP834MappingFunction
 * @see ITURP834WeatherParametersProvider
 * @author Luc Maisonobe
 * @see <a href="https://www.itu.int/rec/R-REC-P.834/en">P.834 : Effects of tropospheric refraction on radiowave propagation</a>
 * @since 13.0
 */
class Gravity {

    /** Name of height reference level. */
    public static final String AVERAGE_HEIGHT_REFERENCE_LEVEL_NAME = "hreflev.dat";

    /** Unit per km. */
    private static final Unit UNIT_PER_KM = Unit.parse("km⁻¹");

    /** Gravity factor for equation 27g. */
    private static final double G_27G = 9.806;

    /** Gravity latitude correction factor for equation 27g. */
    private static final double GL_27G = 0.002637;

    /** Gravity altitude correction factor for equation 27g. */
    private static final double GH_27G = UNIT_PER_KM.toSI(0.00031);

    /** Gravity factor for equation 27j. */
    private static final double G_27J = 9.784;

    /** Gravity latitude correction factor for equation 27j. */
    private static final double GL_27J = 0.00266;

    /** Gravity altitude correction factor for equation 27j. */
    private static final double GH_27J = UNIT_PER_KM.toSI(0.00028);

    /** Gravity at Earth surface. */
    private static final ConstantGrid GS =
        new ConstantGrid(Unit.METRE, AVERAGE_HEIGHT_REFERENCE_LEVEL_NAME).
                apply((lat, lon, h) -> G_27G * (1 - GL_27G * FastMath.cos(2 * lat) - GH_27G * h));

    /** Private constructor for a utility class.
     */
    private Gravity() {
        // nothing to do
    }

    /** Get gravity at surface.
     * @param location point location on Earth
     * @return gravity model over one cell
     */
    public static GridCell getGravityAtSurface(final GeodeticPoint location) {
        return GS.getCell(location, 0.0);
    }

    /** Get gravity at surface.
     * @param <T> type of the field elements
     * @param location point location on Earth
     * @return gravity model over one cell
     */
    public static <T extends CalculusFieldElement<T>> FieldGridCell<T> getGravityAtSurface(final FieldGeodeticPoint<T> location) {
        return GS.getCell(location, null);
    }

    /** Get gravity at point altitude.
     * @param location point location on Earth
     * @return gravity model over one cell
     */
    public static GridCell getGravityAtAltitude(final GeodeticPoint location) {
        final GridAxis latitudeAxis  = GS.getLatitudeAxis();
        final int      southIndex    = latitudeAxis.interpolationIndex(location.getLatitude());
        final double   northLatitude = latitudeAxis.node(southIndex + 1);
        final double   southLatitude = latitudeAxis.node(southIndex);
        final GridAxis longitudeAxis = GS.getLongitudeAxis();
        final int      westIndex     = longitudeAxis.interpolationIndex(location.getLongitude());
        final double   westLongitude = longitudeAxis.node(westIndex);
        final double   mga           = -GH_27J * location.getAltitude();
        final double   gNorth        = (mga + 1 - GL_27J * FastMath.cos(2 * northLatitude)) * G_27J;
        final double   gSouth        = (mga + 1 - GL_27J * FastMath.cos(2 * southLatitude)) * G_27J;
        return new GridCell(location.getLatitude()  - southLatitude,
                            location.getLongitude() - westLongitude,
                            GS.getSizeLat(), GS.getSizeLon(),
                            gNorth, gSouth, gSouth, gNorth);
    }

    /** Get gravity at point altitude.
     * @param <T> type of the field elements
     * @param location point location on Earth
     * @return gravity model over one cell
     */
    public static <T extends CalculusFieldElement<T>> FieldGridCell<T> getGravityAtAltitude(
        final FieldGeodeticPoint<T> location) {
        final GridAxis latitudeAxis  = GS.getLatitudeAxis();
        final int      southIndex    = latitudeAxis.interpolationIndex(location.getLatitude().getReal());
        final double   northLatitude = latitudeAxis.node(southIndex + 1);
        final double   southLatitude = latitudeAxis.node(southIndex);
        final GridAxis longitudeAxis = GS.getLongitudeAxis();
        final int      westIndex     = longitudeAxis.interpolationIndex(location.getLongitude().getReal());
        final double   westLongitude = longitudeAxis.node(westIndex);
        final T        mga           = location.getAltitude().multiply(GH_27J).negate();
        final T        gNorth        = mga.add(1 - GL_27J * FastMath.cos(2 * northLatitude)).multiply(G_27J);
        final T        gSouth        = mga.add(1 - GL_27J * FastMath.cos(2 * southLatitude)).multiply(G_27J);
        return new FieldGridCell<>(location.getLatitude().subtract(southLatitude),
                                   location.getLongitude().subtract(westLongitude),
                                   GS.getSizeLat(), GS.getSizeLon(), gNorth,
                                   gSouth, gSouth, gNorth);
    }

}