Ray.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.util.FastMath;
  19. import org.hipparchus.util.SinCos;
  20. import org.orekit.bodies.GeodeticPoint;

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

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

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

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

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

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

  41.     /** Ray-perigee radius [m]. */
  42.     private final double rp;

  43.     /** Ray-perigee latitude [rad]. */
  44.     private final double latP;

  45.     /** Ray-perigee longitude [rad]. */
  46.     private final double lonP;

  47.     /** Sine and cosine of ray-perigee latitude. */
  48.     private final SinCos scLatP;

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

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

  53.     /**
  54.      * Constructor.
  55.      *
  56.      * @param recP receiver position
  57.      * @param satP satellite position
  58.      */
  59.     public Ray(final GeodeticPoint recP, final GeodeticPoint satP) {

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

  65.         // Useful parameters
  66.         final double lat1     = recP.getLatitude();
  67.         final double lat2     = satP.getLatitude();
  68.         final double lon1     = recP.getLongitude();
  69.         final double lon2     = satP.getLongitude();
  70.         final SinCos scLatSat = FastMath.sinCos(lat2);
  71.         final SinCos scLatRec = FastMath.sinCos(lat1);
  72.         final SinCos scLon21  = FastMath.sinCos(lon2 - lon1);

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

  88.         // Ray-perigee computation in meters (Eq. 156)
  89.         this.rp = r1 * sinZ;

  90.         // Ray-perigee latitude and longitude
  91.         if (FastMath.abs(FastMath.abs(lat1) - 0.5 * FastMath.PI) < THRESHOLD) {
  92.             // receiver is almost at North or South pole

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

  95.             // Ray-perigee longitude (Eq. 164)
  96.             this.lonP = lon2 + FastMath.PI;

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

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

  101.         } else {

  102.             // Ray-perigee latitude (Eq. 158 to 163)
  103.             final double sinAz   = scLon21.sin() * scLatSat.cos() / sinD;
  104.             final double cosAz   = (scLatSat.sin() - cosD * scLatRec.sin()) / (sinD * scLatRec.cos());
  105.             final double sinLatP = scLatRec.sin() * sinZ - scLatRec.cos() * cosZ * cosAz;
  106.             final double cosLatP = FastMath.sqrt(1.0 - sinLatP * sinLatP);
  107.             this.latP = FastMath.atan2(sinLatP, cosLatP);

  108.             // Ray-perigee longitude (Eq. 165 to 167, plus protection against ray-perigee along polar axis)
  109.             if (cosLatP < THRESHOLD) {
  110.                 this.lonP = 0.0;
  111.             } else {
  112.                 final double sinLonP = -sinAz * cosZ / cosLatP;
  113.                 final double cosLonP = (sinZ - scLatRec.sin() * sinLatP) / (scLatRec.cos() * cosLatP);
  114.                 this.lonP = FastMath.atan2(sinLonP, cosLonP) + lon1;
  115.             }

  116.         }

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

  119.         if (FastMath.abs(FastMath.abs(latP) - 0.5 * FastMath.PI) < THRESHOLD || FastMath.abs(sinZ) < THRESHOLD) {
  120.             // Eq. 172 and 173
  121.             this.sinAzP = 0.0;
  122.             this.cosAzP = -FastMath.copySign(1, latP);
  123.         } else {
  124.             final SinCos scLon = FastMath.sinCos(lon2 - lonP);
  125.             // Sine and cosine of azimuth of satellite as seen from ray-perigee
  126.             final SinCos scPsi = FastMath.sinCos(greatCircleAngle(scLatSat, scLon));
  127.             // Eq. 174 and 175
  128.             this.sinAzP = scLatSat.cos() * scLon.sin() / scPsi.sin();
  129.             this.cosAzP = (scLatSat.sin() - scLatP.sin() * scPsi.cos()) / (scLatP.cos() * scPsi.sin());
  130.         }

  131.         // Integration end points s1 and s2 in meters (Eq. 176 and 177)
  132.         this.s1 = FastMath.sqrt(r1 * r1 - rp * rp);
  133.         this.s2 = FastMath.sqrt(r2 * r2 - rp * rp);
  134.     }

  135.     /**
  136.      * Get receiver altitude.
  137.      * @return receiver altitude
  138.      * @since 13.0
  139.      */
  140.     public double getRecH() {
  141.         return recH;
  142.     }

  143.     /**
  144.      * Get satellite altitude.
  145.      * @return satellite altitude
  146.      * @since 13.0
  147.      */
  148.     public double getSatH() {
  149.         return satH;
  150.     }

  151.     /**
  152.      * Get the distance of the first point from the ray perigee.
  153.      *
  154.      * @return s1 in meters
  155.      */
  156.     public double getS1() {
  157.         return s1;
  158.     }

  159.     /**
  160.      * Get the distance of the second point from the ray perigee.
  161.      *
  162.      * @return s2 in meters
  163.      */
  164.     public double getS2() {
  165.         return s2;
  166.     }

  167.     /**
  168.      * Get the ray-perigee radius.
  169.      *
  170.      * @return the ray-perigee radius in meters
  171.      */
  172.     public double getRadius() {
  173.         return rp;
  174.     }

  175.     /**
  176.      * Get the ray-perigee latitude.
  177.      *
  178.      * @return the ray-perigee latitude in radians
  179.      */
  180.     public double getLatitude() {
  181.         return latP;
  182.     }

  183.     /**
  184.      * Get the ray-perigee latitude sin/cos.
  185.      *
  186.      * @return the ray-perigee latitude sin/cos
  187.      * @since 13.0
  188.      */
  189.     public SinCos getScLat() {
  190.         return scLatP;
  191.     }

  192.     /**
  193.      * Get the ray-perigee longitude.
  194.      *
  195.      * @return the ray-perigee longitude in radians
  196.      */
  197.     public double getLongitude() {
  198.         return lonP;
  199.     }

  200.     /**
  201.      * Get the sine of azimuth of satellite as seen from ray-perigee.
  202.      *
  203.      * @return the sine of azimuth
  204.      */
  205.     public double getSineAz() {
  206.         return sinAzP;
  207.     }

  208.     /**
  209.      * Get the cosine of azimuth of satellite as seen from ray-perigee.
  210.      *
  211.      * @return the cosine of azimuth
  212.      */
  213.     public double getCosineAz() {
  214.         return cosAzP;
  215.     }

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

  235. }