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 (Eq. 153 to 155)
  76.         final T cosD = scLatRec.sin().multiply(scLatSat.sin()).
  77.                        add(scLatRec.cos().multiply(scLatSat.cos()).multiply(scLon21.cos()));
  78.         final T sinD = FastMath.sqrt(cosD.multiply(cosD).negate().add(1.0));
  79.         final T z    = FastMath.atan2(sinD, cosD.subtract(r1.divide(r2)));
  80.         final FieldSinCos<T> scZ = FastMath.sinCos(z);

  81.         // Ray-perigee computation in meters (Eq. 156)
  82.         this.rp = r1.multiply(scZ.sin());

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

  85.             // Ray-perigee latitude (Eq. 157)
  86.             this.latP = FastMath.copySign(z, lat1);

  87.             // Ray-perigee longitude (Eq. 164)
  88.             if (z.getReal() < 0) {
  89.                 this.lonP = lon2;
  90.             } else {
  91.                 this.lonP = lon2.add(pi);
  92.             }

  93.         } else if (FastMath.abs(scZ.sin().getReal()) < THRESHOLD) {
  94.             // satellite is almost on receiver zenith

  95.             this.latP = recP.getLatitude();
  96.             this.lonP = recP.getLongitude();

  97.         } else {

  98.             // Ray-perigee latitude (Eq. 158 to 163)
  99.             final T sinAz   = FastMath.sin(lon2.subtract(lon1)).multiply(scLatSat.cos()).divide(sinD);
  100.             final T cosAz   = scLatSat.sin().subtract(cosD.multiply(scLatRec.sin())).
  101.                               divide(sinD.multiply(scLatRec.cos()));
  102.             final T sinLatP = scLatRec.sin().multiply(scZ.sin()).
  103.                               subtract(scLatRec.cos().multiply(scZ.cos()).multiply(cosAz));
  104.             final T cosLatP = FastMath.sqrt(sinLatP.multiply(sinLatP).negate().add(1.0));
  105.             this.latP       = FastMath.atan2(sinLatP, cosLatP);

  106.             // Ray-perigee longitude (Eq. 165 to 167)
  107.             final T sinLonP = sinAz.negate().multiply(scZ.cos()).divide(cosLatP);
  108.             final T cosLonP = scZ.sin().subtract(scLatRec.sin().multiply(sinLatP)).
  109.                               divide(scLatRec.cos().multiply(cosLatP));
  110.             this.lonP       = FastMath.atan2(sinLonP, cosLonP).add(lon1);

  111.         }

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

  114.         if (FastMath.abs(FastMath.abs(latP).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD || FastMath.abs(
  115.             scZ.sin().getReal()) < THRESHOLD) {
  116.             // Eq. 172 and 173
  117.             this.sinAzP = pi.getField().getZero();
  118.             this.cosAzP = FastMath.copySign(pi.getField().getOne(), latP).negate();
  119.         } else {
  120.             final FieldSinCos<T> scLon = FastMath.sinCos(lon2.subtract(lonP));
  121.             // Sine and cosine of azimuth of satellite as seen from ray-perigee
  122.             final FieldSinCos<T> scPsi = FastMath.sinCos(greatCircleAngle(scLatSat, scLon));
  123.             // Eq. 174 and 175
  124.             this.sinAzP = scLatSat.cos().multiply(scLon.sin()).divide(scPsi.sin());
  125.             this.cosAzP = scLatSat.sin().subtract(scLatP.sin().multiply(scPsi.cos())).
  126.                           divide(scLatP.cos().multiply(scPsi.sin()));
  127.         }

  128.         // Integration end points s1 and s2 in meters (Eq. 176 and 177)
  129.         this.s1 = FastMath.sqrt(r1.multiply(r1).subtract(rp.multiply(rp)));
  130.         this.s2 = FastMath.sqrt(r2.multiply(r2).subtract(rp.multiply(rp)));
  131.     }

  132.     /**
  133.      * Get receiver altitude.
  134.      * @return receiver altitude
  135.      * @since 13.0
  136.      */
  137.     public T getRecH() {
  138.         return recH;
  139.     }

  140.     /**
  141.      * Get satellite altitude.
  142.      * @return satellite altitude
  143.      * @since 13.0
  144.      */
  145.     public T getSatH() {
  146.         return satH;
  147.     }

  148.     /**
  149.      * Get the distance of the first point from the ray perigee.
  150.      *
  151.      * @return s1 in meters
  152.      */
  153.     public T getS1() {
  154.         return s1;
  155.     }

  156.     /**
  157.      * Get the distance of the second point from the ray perigee.
  158.      *
  159.      * @return s2 in meters
  160.      */
  161.     public T getS2() {
  162.         return s2;
  163.     }

  164.     /**
  165.      * Get the ray-perigee radius.
  166.      *
  167.      * @return the ray-perigee radius in meters
  168.      */
  169.     public T getRadius() {
  170.         return rp;
  171.     }

  172.     /**
  173.      * Get the ray-perigee latitude.
  174.      *
  175.      * @return the ray-perigee latitude in radians
  176.      */
  177.     public T getLatitude() {
  178.         return latP;
  179.     }

  180.     /**
  181.      * Get the ray-perigee latitude sin/cos.
  182.      *
  183.      * @return the ray-perigee latitude sin/cos
  184.      * @since 13.0
  185.      */
  186.     public FieldSinCos<T> getScLat() {
  187.         return scLatP;
  188.     }

  189.     /**
  190.      * Get the ray-perigee longitude.
  191.      *
  192.      * @return the ray-perigee longitude in radians
  193.      */
  194.     public T getLongitude() {
  195.         return lonP;
  196.     }

  197.     /**
  198.      * Get the sine of azimuth of satellite as seen from ray-perigee.
  199.      *
  200.      * @return the sine of azimuth
  201.      */
  202.     public T getSineAz() {
  203.         return sinAzP;
  204.     }

  205.     /**
  206.      * Get the cosine of azimuth of satellite as seen from ray-perigee.
  207.      *
  208.      * @return the cosine of azimuth
  209.      */
  210.     public T getCosineAz() {
  211.         return cosAzP;
  212.     }

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

  233. }