FieldRay.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.ionosphere.nequick;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.util.FastMath;
  20. import org.hipparchus.util.FieldSinCos;
  21. import org.orekit.bodies.FieldGeodeticPoint;

  22. /** Container for ray-perigee parameters.
  23.  * <p>By convention, point 1 is at lower height.</p>
  24.  * @author Bryan Cazabonne
  25.  * @since 13.0
  26.  */
  27. class FieldRay<T extends CalculusFieldElement<T>> {

  28.     /** Threshold for ray-perigee parameters computation. */
  29.     private static final double THRESHOLD = 1.0e-10;

  30.     /** Receiver altitude [m].
  31.      * @since 13.0
  32.      */
  33.     private final T recH;

  34.     /** Satellite altitude [m].
  35.      * @since 13.0
  36.      */
  37.     private final T satH;

  38.     /** Distance of the first point from the ray perigee [m]. */
  39.     private final T s1;

  40.     /** Distance of the second point from the ray perigee [m]. */
  41.     private final T s2;

  42.     /** Ray-perigee radius [m]. */
  43.     private final T rp;

  44.     /** Ray-perigee latitude [rad]. */
  45.     private final T latP;

  46.     /** Ray-perigee longitude [rad]. */
  47.     private final T lonP;

  48.     /** Sine and cosine of ray-perigee latitude. */
  49.     private final FieldSinCos<T> scLatP;

  50.     /** Sine of azimuth of satellite as seen from ray-perigee. */
  51.     private final T sinAzP;

  52.     /** Cosine of azimuth of satellite as seen from ray-perigee. */
  53.     private final T cosAzP;

  54.     /**
  55.      * Constructor.
  56.      *
  57.      * @param recP  receiver position
  58.      * @param satP  satellite position
  59.      */
  60.     FieldRay(final FieldGeodeticPoint<T> recP, final FieldGeodeticPoint<T> satP) {

  61.         // Integration limits in meters (Eq. 140 and 141)
  62.         this.recH  = recP.getAltitude();
  63.         this.satH  = satP.getAltitude();
  64.         final T r1 = recH.add(NeQuickModel.RE);
  65.         final T r2 = satH.add(NeQuickModel.RE);

  66.         // Useful parameters
  67.         final T pi = r1.getPi();
  68.         final T lat1 = recP.getLatitude();
  69.         final T lat2 = satP.getLatitude();
  70.         final T lon1 = recP.getLongitude();
  71.         final T lon2 = satP.getLongitude();
  72.         final FieldSinCos<T> scLatSat = FastMath.sinCos(lat2);
  73.         final FieldSinCos<T> scLatRec = FastMath.sinCos(lat1);
  74.         final FieldSinCos<T> scLon21 = FastMath.sinCos(lon2.subtract(lon1));

  75.         // Zenith angle computation, using:
  76.         //   - Eq. 153 with added protection against numerical noise near zenith observation
  77.         //   - replacing Eq. 154 by a different one more stable near zenith
  78.         //   - replacing Eq. 155 by different ones avoiding trigonometric functions
  79.         final T cosD     = FastMath.min(r1.getField().getOne(),
  80.                                         scLatRec.sin().multiply(scLatSat.sin()).
  81.                                         add(scLatRec.cos().multiply(scLatSat.cos()).multiply(scLon21.cos())));
  82.         final T sinDSinM = scLatSat.cos().multiply(scLon21.sin());
  83.         final T sinDCosM = scLatSat.sin().multiply(scLatRec.cos()).subtract(scLatSat.cos().multiply(scLatRec.sin()).multiply(scLon21.cos()));
  84.         final T sinD     = FastMath.sqrt(sinDSinM.multiply(sinDSinM).add(sinDCosM.multiply(sinDCosM)));
  85.         final T u        = r2.multiply(sinD);
  86.         final T v        = r2.multiply(cosD).subtract(r1);
  87.         final T inv      = FastMath.sqrt(u.multiply(u).add(v.multiply(v))).reciprocal();
  88.         final T sinZ     = u.multiply(inv);
  89.         final T cosZ     = v.multiply(inv);

  90.         // Ray-perigee computation in meters (Eq. 156)
  91.         this.rp = r1.multiply(sinZ);

  92.         // Ray-perigee latitude and longitude
  93.         if (FastMath.abs(FastMath.abs(lat1).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD) {

  94.             // Ray-perigee latitude (Eq. 157)
  95.             this.latP = FastMath.copySign(FastMath.atan2(u, v), lat1);

  96.             // Ray-perigee longitude (Eq. 164)
  97.             this.lonP = lon2.add(pi);

  98.         } else if (FastMath.abs(sinZ.getReal()) < THRESHOLD) {
  99.             // satellite is almost on receiver zenith

  100.             this.latP = recP.getLatitude();
  101.             this.lonP = recP.getLongitude();

  102.         } else {

  103.             // Ray-perigee latitude (Eq. 158 to 163)
  104.             final T sinAz   = FastMath.sin(lon2.subtract(lon1)).multiply(scLatSat.cos()).divide(sinD);
  105.             final T cosAz   = scLatSat.sin().subtract(cosD.multiply(scLatRec.sin())).
  106.                               divide(sinD.multiply(scLatRec.cos()));
  107.             final T sinLatP = scLatRec.sin().multiply(sinZ).
  108.                               subtract(scLatRec.cos().multiply(cosZ).multiply(cosAz));
  109.             final T cosLatP = FastMath.sqrt(sinLatP.multiply(sinLatP).negate().add(1.0));
  110.             this.latP       = FastMath.atan2(sinLatP, cosLatP);

  111.             // Ray-perigee longitude (Eq. 165 to 167, plus protection against ray-perigee along polar axis)
  112.             if (cosLatP.getReal() < THRESHOLD) {
  113.                 this.lonP = cosLatP.getField().getZero();
  114.             } else {
  115.                 final T sinLonP = sinAz.negate().multiply(cosZ).divide(cosLatP);
  116.                 final T cosLonP = sinZ.subtract(scLatRec.sin().multiply(sinLatP)).
  117.                         divide(scLatRec.cos().multiply(cosLatP));
  118.                 this.lonP = FastMath.atan2(sinLonP, cosLonP).add(lon1);
  119.             }

  120.         }

  121.         // Sine and cosine of ray-perigee latitude
  122.         this.scLatP = FastMath.sinCos(latP);

  123.         if (FastMath.abs(FastMath.abs(latP).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD || FastMath.abs(
  124.             sinZ.getReal()) < THRESHOLD) {
  125.             // Eq. 172 and 173
  126.             this.sinAzP = pi.getField().getZero();
  127.             this.cosAzP = FastMath.copySign(pi.getField().getOne(), latP).negate();
  128.         } else {
  129.             final FieldSinCos<T> scLon = FastMath.sinCos(lon2.subtract(lonP));
  130.             // Sine and cosine of azimuth of satellite as seen from ray-perigee
  131.             final FieldSinCos<T> scPsi = FastMath.sinCos(greatCircleAngle(scLatSat, scLon));
  132.             // Eq. 174 and 175
  133.             this.sinAzP = scLatSat.cos().multiply(scLon.sin()).divide(scPsi.sin());
  134.             this.cosAzP = scLatSat.sin().subtract(scLatP.sin().multiply(scPsi.cos())).
  135.                           divide(scLatP.cos().multiply(scPsi.sin()));
  136.         }

  137.         // Integration end points s1 and s2 in meters (Eq. 176 and 177)
  138.         this.s1 = FastMath.sqrt(r1.multiply(r1).subtract(rp.multiply(rp)));
  139.         this.s2 = FastMath.sqrt(r2.multiply(r2).subtract(rp.multiply(rp)));
  140.     }

  141.     /**
  142.      * Get receiver altitude.
  143.      * @return receiver altitude
  144.      * @since 13.0
  145.      */
  146.     public T getRecH() {
  147.         return recH;
  148.     }

  149.     /**
  150.      * Get satellite altitude.
  151.      * @return satellite altitude
  152.      * @since 13.0
  153.      */
  154.     public T getSatH() {
  155.         return satH;
  156.     }

  157.     /**
  158.      * Get the distance of the first point from the ray perigee.
  159.      *
  160.      * @return s1 in meters
  161.      */
  162.     public T getS1() {
  163.         return s1;
  164.     }

  165.     /**
  166.      * Get the distance of the second point from the ray perigee.
  167.      *
  168.      * @return s2 in meters
  169.      */
  170.     public T getS2() {
  171.         return s2;
  172.     }

  173.     /**
  174.      * Get the ray-perigee radius.
  175.      *
  176.      * @return the ray-perigee radius in meters
  177.      */
  178.     public T getRadius() {
  179.         return rp;
  180.     }

  181.     /**
  182.      * Get the ray-perigee latitude.
  183.      *
  184.      * @return the ray-perigee latitude in radians
  185.      */
  186.     public T getLatitude() {
  187.         return latP;
  188.     }

  189.     /**
  190.      * Get the ray-perigee latitude sin/cos.
  191.      *
  192.      * @return the ray-perigee latitude sin/cos
  193.      * @since 13.0
  194.      */
  195.     public FieldSinCos<T> getScLat() {
  196.         return scLatP;
  197.     }

  198.     /**
  199.      * Get the ray-perigee longitude.
  200.      *
  201.      * @return the ray-perigee longitude in radians
  202.      */
  203.     public T getLongitude() {
  204.         return lonP;
  205.     }

  206.     /**
  207.      * Get the sine of azimuth of satellite as seen from ray-perigee.
  208.      *
  209.      * @return the sine of azimuth
  210.      */
  211.     public T getSineAz() {
  212.         return sinAzP;
  213.     }

  214.     /**
  215.      * Get the cosine of azimuth of satellite as seen from ray-perigee.
  216.      *
  217.      * @return the cosine of azimuth
  218.      */
  219.     public T getCosineAz() {
  220.         return cosAzP;
  221.     }

  222.     /**
  223.      * Compute the great circle angle from ray-perigee to satellite.
  224.      * <p>
  225.      * This method used the equations 168 to 171 of the reference document.
  226.      * </p>
  227.      *
  228.      * @param scLat sine and cosine of satellite latitude
  229.      * @param scLon sine and cosine of satellite longitude minus receiver longitude
  230.      * @return the great circle angle in radians
  231.      */
  232.     private T greatCircleAngle(final FieldSinCos<T> scLat, final FieldSinCos<T> scLon) {
  233.         if (FastMath.abs(FastMath.abs(latP).getReal() - 0.5 * FastMath.PI) < THRESHOLD) {
  234.             return FastMath.abs(FastMath.asin(scLat.sin()).subtract(latP));
  235.         } else {
  236.             final T cosPhi = scLatP.sin().multiply(scLat.sin()).
  237.                              add(scLatP.cos().multiply(scLat.cos()).multiply(scLon.cos()));
  238.             final T sinPhi = FastMath.sqrt(cosPhi.multiply(cosPhi).negate().add(1.0));
  239.             return FastMath.atan2(sinPhi, cosPhi);
  240.         }
  241.     }

  242. }