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 (Eq. 153 to 155)
  74.         // with added protection against numerical noise near zenith observation
  75.         final double cosD = FastMath.min(1.0,
  76.                                          scLatRec.sin() * scLatSat.sin() +
  77.                                          scLatRec.cos() * scLatSat.cos() * scLon21.cos());
  78.         final double sinD = FastMath.sqrt(1.0 - cosD * cosD);
  79.         final double z    = FastMath.atan2(sinD, cosD - (r1 / r2));
  80.         final SinCos scZ  = FastMath.sinCos(z);

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

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

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

  88.             // Ray-perigee longitude (Eq. 164)
  89.             if (z < 0) {
  90.                 this.lonP = lon2;
  91.             } else {
  92.                 this.lonP = lon2 + FastMath.PI;
  93.             }

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

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

  98.         } else {

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

  105.             // Ray-perigee longitude (Eq. 165 to 167)
  106.             final double sinLonP = -sinAz * scZ.cos() / cosLatP;
  107.             final double cosLonP = (scZ.sin() - scLatRec.sin() * sinLatP) / (scLatRec.cos() * cosLatP);
  108.             this.lonP = FastMath.atan2(sinLonP, cosLonP) + lon1;

  109.         }

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

  112.         if (FastMath.abs(FastMath.abs(latP) - 0.5 * FastMath.PI) < THRESHOLD || FastMath.abs(scZ.sin()) < THRESHOLD) {
  113.             // Eq. 172 and 173
  114.             this.sinAzP = 0.0;
  115.             this.cosAzP = -FastMath.copySign(1, latP);
  116.         } else {
  117.             final SinCos scLon = FastMath.sinCos(lon2 - lonP);
  118.             // Sine and cosine of azimuth of satellite as seen from ray-perigee
  119.             final SinCos scPsi = FastMath.sinCos(greatCircleAngle(scLatSat, scLon));
  120.             // Eq. 174 and 175
  121.             this.sinAzP = scLatSat.cos() * scLon.sin() / scPsi.sin();
  122.             this.cosAzP = (scLatSat.sin() - scLatP.sin() * scPsi.cos()) / (scLatP.cos() * scPsi.sin());
  123.         }

  124.         // Integration end points s1 and s2 in meters (Eq. 176 and 177)
  125.         this.s1 = FastMath.sqrt(r1 * r1 - rp * rp);
  126.         this.s2 = FastMath.sqrt(r2 * r2 - rp * rp);
  127.     }

  128.     /**
  129.      * Get receiver altitude.
  130.      * @return receiver altitude
  131.      * @since 13.0
  132.      */
  133.     public double getRecH() {
  134.         return recH;
  135.     }

  136.     /**
  137.      * Get satellite altitude.
  138.      * @return satellite altitude
  139.      * @since 13.0
  140.      */
  141.     public double getSatH() {
  142.         return satH;
  143.     }

  144.     /**
  145.      * Get the distance of the first point from the ray perigee.
  146.      *
  147.      * @return s1 in meters
  148.      */
  149.     public double getS1() {
  150.         return s1;
  151.     }

  152.     /**
  153.      * Get the distance of the second point from the ray perigee.
  154.      *
  155.      * @return s2 in meters
  156.      */
  157.     public double getS2() {
  158.         return s2;
  159.     }

  160.     /**
  161.      * Get the ray-perigee radius.
  162.      *
  163.      * @return the ray-perigee radius in meters
  164.      */
  165.     public double getRadius() {
  166.         return rp;
  167.     }

  168.     /**
  169.      * Get the ray-perigee latitude.
  170.      *
  171.      * @return the ray-perigee latitude in radians
  172.      */
  173.     public double getLatitude() {
  174.         return latP;
  175.     }

  176.     /**
  177.      * Get the ray-perigee latitude sin/cos.
  178.      *
  179.      * @return the ray-perigee latitude sin/cos
  180.      * @since 13.0
  181.      */
  182.     public SinCos getScLat() {
  183.         return scLatP;
  184.     }

  185.     /**
  186.      * Get the ray-perigee longitude.
  187.      *
  188.      * @return the ray-perigee longitude in radians
  189.      */
  190.     public double getLongitude() {
  191.         return lonP;
  192.     }

  193.     /**
  194.      * Get the sine of azimuth of satellite as seen from ray-perigee.
  195.      *
  196.      * @return the sine of azimuth
  197.      */
  198.     public double getSineAz() {
  199.         return sinAzP;
  200.     }

  201.     /**
  202.      * Get the cosine of azimuth of satellite as seen from ray-perigee.
  203.      *
  204.      * @return the cosine of azimuth
  205.      */
  206.     public double getCosineAz() {
  207.         return cosAzP;
  208.     }

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

  228. }