JB2006.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.forces.drag;

  18. import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  19. import org.apache.commons.math3.util.FastMath;
  20. import org.orekit.bodies.BodyShape;
  21. import org.orekit.bodies.GeodeticPoint;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitMessages;
  24. import org.orekit.frames.Frame;
  25. import org.orekit.frames.Transform;
  26. import org.orekit.time.AbsoluteDate;
  27. import org.orekit.utils.Constants;
  28. import org.orekit.utils.PVCoordinates;
  29. import org.orekit.utils.PVCoordinatesProvider;

  30. /** This is the realization of the Jacchia-Bowman 2006 atmospheric model.
  31.  * <p>
  32.  * It is described in the paper: <br>
  33.  *
  34.  * <a href="http://sol.spacenvironment.net/~JB2006/pubs/JB2006_AIAA-6166_model.pdf">A
  35.  * New Empirical Thermospheric Density Model JB2006 Using New Solar Indices</a><br>
  36.  *
  37.  * <i>Bruce R. Bowman, W. Kent Tobiska and Frank A. Marcos</i> <br>
  38.  *
  39.  * AIAA 2006-6166<br>
  40.  *</p>
  41.  * <p>
  42.  * Two computation methods are proposed to the user:
  43.  * <ul>
  44.  * <li> one OREKIT independent and compliant with initial FORTRAN routine entry values:
  45.  *        {@link #getDensity(double, double, double, double, double, double, double, double, double, double, double, double, double)}. </li>
  46.  * <li> one compliant with OREKIT Atmosphere interface, necessary to the
  47.  *        {@link org.orekit.forces.drag.DragForce
  48.  *        drag force model} computation.</li>
  49.  * </ul>
  50.  * </p>
  51.  * <p>
  52.  * This model provides dense output for all altitudes and positions. Output data are :
  53.  * <ul>
  54.  * <li>Exospheric Temperature above Input Position (deg K)</li>
  55.  * <li>Temperature at Input Position (deg K)</li>
  56.  * <li>Total Mass-Density at Input Position (kg/m<sup>3</sup>)</li>
  57.  * </ul>
  58.  * </p>
  59.  * <p>
  60.  * The model needs geographical and time information to compute general values,
  61.  * but also needs space weather data : mean and daily solar flux, retrieved threw
  62.  * different indices, and planetary geomagnetic indices. <br>
  63.  * More information on these indices can be found on the  <a
  64.  * href="http://sol.spacenvironment.net/~JB2006/JB2006_index.html">
  65.  * official JB2006 website.</a>
  66.  *</p>
  67.  *
  68.  * @author Bruce R Bowman (HQ AFSPC, Space Analysis Division), Feb 2006: FORTRAN routine
  69.  * @author Fabien Maussion (java translation)
  70.  */
  71. public class JB2006 implements Atmosphere {

  72.     /** Serializable UID. */
  73.     private static final long serialVersionUID = -4201270765122160831L;

  74.     /** The alpha are the thermal diffusion coefficients in equation (6). */
  75.     private static final double[] ALPHA = {
  76.         0, 0, 0, 0, 0, -0.38
  77.     };

  78.     /** Natural logarithm of 10.0. */
  79.     private static final double AL10  = 2.3025851;

  80.     /** Molecular weights in order: N2, O2, O, Ar, He and H. */
  81.     private static final double[] AMW = {
  82.         0,
  83.         28.0134, 31.9988, 15.9994, 39.9480, 4.0026, 1.00797
  84.     };

  85.     /** Avogadro's number in mks units (molecules/kmol). */
  86.     private static final double AVOGAD = 6.02257e26;

  87.     /** Approximate value for 2 &pi;. */
  88.     private static final double TWOPI   = 6.2831853;

  89.     /** Approximate value for &pi;. */
  90.     private static final double PI      = 3.1415927;

  91.     /** Approximate value for &pi; / 2. */
  92.     private static final double PIOV2   = 1.5707963;

  93.     /** The FRAC are the assumed sea-level volume fractions in order: N2, O2, Ar, and He. */
  94.     private static final double[] FRAC = {
  95.         0,
  96.         0.78110, 0.20955, 9.3400e-3, 1.2890e-5
  97.     };

  98.     /** Universal gas-constant in mks units (joules/K/kmol). */
  99.     private static final double RSTAR = 8314.32;

  100.     /** Value used to establish height step sizes in the regime 90km to 105km. */
  101.     private static final double R1 = 0.010;

  102.     /** Value used to establish height step sizes in the regime 105km to 500km. */
  103.     private static final double R2 = 0.025;

  104.     /** Value used to establish height step sizes in the regime above 500km. */
  105.     private static final double R3 = 0.075;

  106.     /** Weights for the Newton-Cotes five-points quadrature formula. */
  107.     private static final double[] WT = {
  108.         0,
  109.         0.311111111111111, 1.422222222222222,
  110.         0.533333333333333, 1.422222222222222,
  111.         0.311111111111111
  112.     };

  113.     /** Coefficients for high altitude density correction. */
  114.     private static final double[] CHT = {
  115.         0, 0.22, -0.20e-02, 0.115e-02, -0.211e-05
  116.     };

  117.     /** FZ global model values (1978-2004 fit).  */
  118.     private static final double[] FZM = {
  119.         0,
  120.         0.111613e+00, -0.159000e-02, 0.126190e-01,
  121.         -0.100064e-01, -0.237509e-04, 0.260759e-04
  122.     };

  123.     /** GT global model values (1978-2004 fit). */
  124.     private static final double[] GTM = {
  125.         0,
  126.         -0.833646e+00, -0.265450e+00, 0.467603e+00, -0.299906e+00,
  127.         -0.105451e+00, -0.165537e-01, -0.380037e-01, -0.150991e-01,
  128.         -0.541280e-01, 0.119554e-01, 0.437544e-02, -0.369016e-02,
  129.         0.206763e-02, -0.142888e-02, -0.867124e-05, 0.189032e-04,
  130.         0.156988e-03,  0.491286e-03, -0.391484e-04, -0.126854e-04,
  131.         0.134078e-04, -0.614176e-05, 0.343423e-05
  132.     };

  133.     /** XAMBAR relative data. */
  134.     private static final double[] CXAMB = {
  135.         0,
  136.         28.15204, -8.5586e-2, +1.2840e-4, -1.0056e-5,
  137.         -1.0210e-5, +1.5044e-6, +9.9826e-8
  138.     };

  139.     /** DTSUB relative data. */
  140.     private static final double[] BDT_SUB = {
  141.         0,
  142.         -0.457512297e+01, -0.512114909e+01, -0.693003609e+02,
  143.         0.203716701e+03,  0.703316291e+03, -0.194349234e+04,
  144.         0.110651308e+04, -0.174378996e+03,  0.188594601e+04,
  145.         -0.709371517e+04,  0.922454523e+04, -0.384508073e+04,
  146.         -0.645841789e+01,  0.409703319e+02, -0.482006560e+03,
  147.         0.181870931e+04, -0.237389204e+04,  0.996703815e+03,
  148.         0.361416936e+02
  149.     };

  150.     /** DTSUB relative data. */
  151.     private static final double[] CDT_SUB = {
  152.         0,
  153.         -0.155986211e+02, -0.512114909e+01, -0.693003609e+02,
  154.         0.203716701e+03,  0.703316291e+03, -0.194349234e+04,
  155.         0.110651308e+04, -0.220835117e+03,  0.143256989e+04,
  156.         -0.318481844e+04,  0.328981513e+04, -0.135332119e+04,
  157.         0.199956489e+02, -0.127093998e+02,  0.212825156e+02,
  158.         -0.275555432e+01,  0.110234982e+02,  0.148881951e+03,
  159.         -0.751640284e+03,  0.637876542e+03,  0.127093998e+02,
  160.         -0.212825156e+02,  0.275555432e+01
  161.     };

  162.     /** Temperatures.
  163.      *  <p><ul>
  164.      *  <li>TEMP(1): Exospheric Temperature above Input Position (deg K)</li>
  165.      *  <li>TEMP(2): Temperature at Input Position (deg K)</li>
  166.      *  </ul></p>
  167.      */
  168.     private double[] temp = new double[3];

  169.     /** Total Mass-Density at Input Position (kg/m<sup>3</sup>). */
  170.     private double rho;

  171.     /** Sun position. */
  172.     private PVCoordinatesProvider sun;

  173.     /** External data container. */
  174.     private JB2006InputParameters inputParams;

  175.     /** Earth body shape. */
  176.     private BodyShape earth;

  177.     /** Constructor with space environment information for internal computation.
  178.      * @param parameters the solar and magnetic activity data
  179.      * @param sun the sun position
  180.      * @param earth the earth body shape
  181.      */
  182.     public JB2006(final JB2006InputParameters parameters,
  183.                   final PVCoordinatesProvider sun, final BodyShape earth) {
  184.         this.earth = earth;
  185.         this.sun = sun;
  186.         this.inputParams = parameters;
  187.     }

  188.     /** {@inheritDoc} */
  189.     public Frame getFrame() {
  190.         return earth.getBodyFrame();
  191.     }

  192.     /** Get the local density with initial entries.
  193.      * @param dateMJD date and time, in modified julian days and fraction
  194.      * @param sunRA Right Ascension of Sun (radians)
  195.      * @param sunDecli Declination of Sun (radians)
  196.      * @param satLon Right Ascension of position (radians)
  197.      * @param satLat Geocentric latitude of position (radians)
  198.      * @param satAlt Height of position (m)
  199.      * @param f10 10.7-cm Solar flux (1e<sup>-22</sup>*Watt/(m<sup>2</sup>*Hertz)).
  200.      *            Tabular time 1.0 day earlier
  201.      * @param f10B 10.7-cm Solar Flux, averaged 81-day centered on the input time
  202.      * @param ap Geomagnetic planetary 3-hour index A<sub>p</sub>
  203.      *            for a tabular time 6.7 hours earlier
  204.      * @param s10 EUV index (26-34 nm) scaled to F10. Tabular time 1 day earlier.
  205.      * @param s10B UV 81-day averaged centered index
  206.      * @param xm10 MG2 index scaled to F10
  207.      * @param xm10B MG2 81-day ave. centered index. Tabular time 5.0 days earlier.
  208.      * @return total mass-Density at input position (kg/m<sup>3</sup>)
  209.      */
  210.     public double getDensity(final double dateMJD, final double sunRA, final double sunDecli,
  211.                              final double satLon, final double satLat, final double satAlt,
  212.                              final double f10, final double f10B, final double ap,
  213.                              final double s10, final double s10B, final double xm10, final double xm10B) {

  214.         final double scaledSatAlt = satAlt / 1000.0;

  215.         // Equation (14)
  216.         final double tc = 379 + 3.353 * f10B + 0.358 * (f10 - f10B) +
  217.                           2.094 * (s10 - s10B) + 0.343 * (xm10 - xm10B);

  218.         // Equation (15)
  219.         final double eta =   0.5 * FastMath.abs(satLat - sunDecli);
  220.         final double theta = 0.5 * FastMath.abs(satLat + sunDecli);

  221.         // Equation (16)
  222.         final double h     = satLon - sunRA;
  223.         final double tau   = h - 0.64577182 + 0.10471976 * FastMath.sin(h + 0.75049158);
  224.         double solTimeHour = FastMath.toDegrees(h + PI) / 15.0;
  225.         if (solTimeHour >= 24) {
  226.             solTimeHour = solTimeHour - 24.;
  227.         }
  228.         if (solTimeHour < 0) {
  229.             solTimeHour = solTimeHour + 24.;
  230.         }

  231.         // Equation (17)
  232.         final double C = FastMath.pow(FastMath.cos(eta), 2.5);
  233.         final double S = FastMath.pow(FastMath.sin(theta), 2.5);
  234.         final double tmp = FastMath.abs(FastMath.cos(0.5 * tau));
  235.         final double DF = S + (C - S) * tmp * tmp * tmp;
  236.         final double TSUBL = tc * (1. + 0.31 * DF);

  237.         // Equation (18)
  238.         final double EXPAP = FastMath.exp(-0.08 * ap);
  239.         final double DTG = ap + 100. * (1. - EXPAP);

  240.         // Compute correction to dTc for local solar time and lat correction
  241.         final double DTCLST = dTc(f10, solTimeHour, satLat, scaledSatAlt);

  242.         // Compute the local exospheric temperature.
  243.         final double TINF = TSUBL + DTG + DTCLST;
  244.         temp[1] = TINF;

  245.         // Equation (9)
  246.         final double TSUBX = 444.3807 + 0.02385 * TINF - 392.8292 * FastMath.exp(-0.0021357 * TINF);

  247.         // Equation (11)
  248.         final double GSUBX = 0.054285714 * (TSUBX - 183.);

  249.         // The TC array will be an argument in the call to
  250.         // XLOCAL, which evaluates Equation (10) or Equation (13)
  251.         final double[] TC = new double[4];
  252.         TC[0] = TSUBX;
  253.         TC[1] = GSUBX;

  254.         //   A AND GSUBX/A OF Equation (13)
  255.         TC[2] = (TINF - TSUBX) / PIOV2;
  256.         TC[3] = GSUBX / TC[2];

  257.         // Equation (5)
  258.         final double Z1 = 90.;
  259.         final double Z2 = FastMath.min(scaledSatAlt, 105.0);
  260.         double AL = FastMath.log(Z2 / Z1);
  261.         int N = (int) FastMath.floor(AL / R1) + 1;
  262.         double ZR = FastMath.exp(AL / N);
  263.         final double AMBAR1 = xAmbar(Z1);
  264.         final double TLOC1 = xLocal(Z1, TC);
  265.         double ZEND   = Z1;
  266.         double SUM2   = 0.;
  267.         double AIN    = AMBAR1 * xGrav(Z1) / TLOC1;
  268.         double AMBAR2 = 0;
  269.         double TLOC2  = 0;
  270.         double Z      = 0;
  271.         double GRAVL  = 0;

  272.         for (int i = 1; i <= N; ++i) {
  273.             Z = ZEND;
  274.             ZEND = ZR * Z;
  275.             final double DZ = 0.25 * (ZEND - Z);
  276.             double SUM1 = WT[1] * AIN;
  277.             for (int j = 2; j <= 5; ++j) {
  278.                 Z += DZ;
  279.                 AMBAR2 = xAmbar(Z);
  280.                 TLOC2  = xLocal(Z, TC);
  281.                 GRAVL  = xGrav(Z);
  282.                 AIN    = AMBAR2 * GRAVL / TLOC2;
  283.                 SUM1  += WT[j] * AIN;
  284.             }
  285.             SUM2 = SUM2 + DZ * SUM1;
  286.         }
  287.         final double FACT1 = 1000.0 / RSTAR;
  288.         rho = 3.46e-6 * AMBAR2 * TLOC1 * FastMath.exp(-FACT1 * SUM2) / (AMBAR1 * TLOC2);

  289.         // Equation (2)
  290.         final double ANM = AVOGAD * rho;
  291.         double AN  = ANM / AMBAR2;

  292.         // Equation (3)
  293.         double FACT2  = ANM / 28.960;
  294.         final double[] ALN = new double[7];
  295.         ALN[1] = FastMath.log(FRAC[1] * FACT2);
  296.         ALN[4] = FastMath.log(FRAC[3] * FACT2);
  297.         ALN[5] = FastMath.log(FRAC[4] * FACT2);

  298.         // Equation (4)
  299.         ALN[2] = FastMath.log(FACT2 * (1. + FRAC[2]) - AN);
  300.         ALN[3] = FastMath.log(2. * (AN - FACT2));

  301.         if (scaledSatAlt <= 105.0) {
  302.             temp[2] = TLOC2;
  303.             // Put in negligible hydrogen for use in DO-LOOP 13
  304.             ALN[6] = ALN[5] - 25.0;
  305.         } else {
  306.             // Equation (6)
  307.             final double Z3 = FastMath.min(scaledSatAlt, 500.0);
  308.             AL   = FastMath.log(Z3 / Z);
  309.             N    = (int) FastMath.floor(AL / R2) + 1;
  310.             ZR   = FastMath.exp(AL / N);
  311.             SUM2 = 0.;
  312.             AIN  = GRAVL / TLOC2;

  313.             double TLOC3 = 0;
  314.             for (int I = 1; I <= N; ++I) {
  315.                 Z = ZEND;
  316.                 ZEND = ZR * Z;
  317.                 final double DZ = 0.25 * (ZEND - Z);
  318.                 double SUM1 = WT[1] * AIN;
  319.                 for (int J = 2; J <= 5; ++J) {
  320.                     Z    += DZ;
  321.                     TLOC3 = xLocal(Z, TC);
  322.                     GRAVL = xGrav(Z);
  323.                     AIN   = GRAVL / TLOC3;
  324.                     SUM1  = SUM1 + WT[J] * AIN;
  325.                 }
  326.                 SUM2 = SUM2 + DZ * SUM1;
  327.             }

  328.             final double Z4 = FastMath.max(scaledSatAlt, 500.0);
  329.             AL = FastMath.log(Z4 / Z);
  330.             double R = R2;
  331.             if (scaledSatAlt > 500.0) {
  332.                 R = R3;
  333.             }
  334.             N = (int) FastMath.floor(AL / R) + 1;
  335.             ZR = FastMath.exp(AL / N);
  336.             double SUM3 = 0.;
  337.             double TLOC4 = 0;
  338.             for (int I = 1; I <= N; ++I) {
  339.                 Z = ZEND;
  340.                 ZEND = ZR * Z;
  341.                 final double DZ = 0.25 * (ZEND - Z);
  342.                 double SUM1 = WT[1] * AIN;
  343.                 for (int J = 2; J <= 5; ++J) {
  344.                     Z    += DZ;
  345.                     TLOC4 = xLocal(Z, TC);
  346.                     GRAVL = xGrav(Z);
  347.                     AIN   = GRAVL / TLOC4;
  348.                     SUM1  = SUM1 + WT[J] * AIN;
  349.                 }
  350.                 SUM3 = SUM3 + DZ * SUM1;
  351.             }
  352.             double ALTR;
  353.             double HSIGN;
  354.             if (scaledSatAlt <= 500.) {
  355.                 temp[2] = TLOC3;
  356.                 ALTR = FastMath.log(TLOC3 / TLOC2);
  357.                 FACT2 = FACT1 * SUM2;
  358.                 HSIGN = 1.0;

  359.             } else {
  360.                 temp[2] = TLOC4;
  361.                 ALTR = FastMath.log(TLOC4 / TLOC2);
  362.                 FACT2 = FACT1 * (SUM2 + SUM3);
  363.                 HSIGN = -1.0;
  364.             }
  365.             for (int I = 1; I <= 5; ++I) {
  366.                 ALN[I] = ALN[I] - (1.0 + ALPHA[I]) * ALTR - FACT2 * AMW[I];
  367.             }

  368.             // Equation (7) - Note that in CIRA72, AL10T5 = DLOG10(T500)
  369.             final double AL10T5 = FastMath.log(TINF) / FastMath.log(10);
  370.             final double ALNH5 = (5.5 * AL10T5 - 39.40) * AL10T5 + 73.13;
  371.             ALN[6] = AL10 * (ALNH5 + 6.) + HSIGN * (FastMath.log(TLOC4 / TLOC3) + FACT1 * SUM3 * AMW[6]);

  372.         }

  373.         // Equation (24)  - J70 Seasonal-Latitudinal Variation
  374.         final double CAPPHI = ((dateMJD - 36204.0) / 365.2422) % 1;
  375.         final int signum = (satLat >= 0) ? 1 : -1;
  376.         final double sinLat = FastMath.sin(satLat);
  377.         final double DLRSL = 0.02 * (scaledSatAlt - 90.) * FastMath.exp(-0.045 * (scaledSatAlt - 90.)) *
  378.                              signum * FastMath.sin(TWOPI * CAPPHI + 1.72) * sinLat * sinLat;

  379.         // Equation (23) - Computes the semiannual variation
  380.         double DLRSA = 0;
  381.         if (Z < 2000.0) {
  382.             final double D1950 = dateMJD - 33281.0;
  383.             // Use new semiannual model DELTA LOG RHO
  384.             DLRSA = semian(dayOfYear(D1950), scaledSatAlt, f10B);
  385.         }

  386.         // Sum the delta-log-rhos and apply to the number densities.
  387.         // In CIRA72 the following equation contains an actual sum,
  388.         // namely DLR = AL10 * (DLRGM + DLRSA + DLRSL)
  389.         // However, for Jacchia 70, there is no DLRGM or DLRSA.
  390.         final double DLR = AL10 * (DLRSL + DLRSA);
  391.         for (int i = 1; i <= 6; ++i) {
  392.             ALN[i] += DLR;
  393.         }

  394.         // Compute mass-density and mean-molecular-weight and
  395.         // convert number density logs from natural to common.

  396.         double SUMNM = 0.0;

  397.         for (int I = 1; I <= 6; ++I) {
  398.             AN = FastMath.exp(ALN[I]);
  399.             SUMNM += AN * AMW[I];
  400.         }

  401.         rho = SUMNM / AVOGAD;

  402.         // Compute the high altitude exospheric density correction factor
  403.         double FEX = 1.0;
  404.         if ((scaledSatAlt >= 1000.0) && (scaledSatAlt < 1500.0)) {
  405.             final double ZETA   = (scaledSatAlt - 1000.) * 0.002;
  406.             final double ZETA2  =  ZETA * ZETA;
  407.             final double ZETA3  =  ZETA * ZETA2;
  408.             final double F15C   = CHT[1] + CHT[2] * f10B + CHT[3] * 1500.0 + CHT[4] * f10B * 1500.0;
  409.             final double F15C_ZETA = (CHT[3] + CHT[4] * f10B) * 500.0;
  410.             final double FEX2   = 3.0 * F15C - F15C_ZETA - 3.0;
  411.             final double FEX3   = F15C_ZETA - 2.0 * F15C + 2.0;
  412.             FEX    = 1.0 + FEX2 * ZETA2 + FEX3 * ZETA3;
  413.         }
  414.         if (scaledSatAlt >= 1500.0) {
  415.             FEX    = CHT[1] + CHT[2] * f10B + CHT[3] * scaledSatAlt + CHT[4] * f10B * scaledSatAlt;
  416.         }

  417.         // Apply the exospheric density correction factor.
  418.         rho  *= FEX;

  419.         return rho;

  420.     }

  421.     /** Compute daily temperature correction for Jacchia-Bowman model.
  422.      * @param f10 solar flux index
  423.      * @param solTimeHour local solar time (hours 0-23.999)
  424.      * @param satLat sat lat (radians)
  425.      * @param satAlt height (km)
  426.      * @return dTc correction
  427.      */
  428.     private static double dTc(final double f10, final double solTimeHour,
  429.                               final double satLat, final double satAlt) {

  430.         double dTc = 0;
  431.         final double tx  = solTimeHour / 24.0;
  432.         final double tx2 = tx * tx;
  433.         final double tx3 = tx2 * tx;
  434.         final double tx4 = tx3 * tx;
  435.         final double tx5 = tx4 * tx;
  436.         final double ycs = FastMath.cos(satLat);
  437.         final double f   = (f10 - 100.0) / 100.0;
  438.         double h;
  439.         double sum;

  440.         // Calculates dTc
  441.         if ((satAlt >= 120) && (satAlt <= 200)) {
  442.             final double DTC200 = CDT_SUB[17] + CDT_SUB[18] * tx * ycs + CDT_SUB[19] * tx2 * ycs +
  443.                                   CDT_SUB[20] * tx3 * ycs + CDT_SUB[21] * f * ycs + CDT_SUB[22] * tx * f * ycs +
  444.                                   CDT_SUB[23] * tx2 * f * ycs;
  445.             sum = CDT_SUB[1] + BDT_SUB[2] * f + CDT_SUB[3] * tx * f + CDT_SUB[4] * tx2 * f +
  446.                   CDT_SUB[5] * tx3 * f + CDT_SUB[6] * tx4 * f + CDT_SUB[7] * tx5 * f +
  447.                   CDT_SUB[8] * tx * ycs + CDT_SUB[9] * tx2 * ycs + CDT_SUB[10] * tx3 * ycs +
  448.                   CDT_SUB[11] * tx4 * ycs + CDT_SUB[12] * tx5 * ycs + CDT_SUB[13] * ycs +
  449.                   CDT_SUB[14] * f * ycs + CDT_SUB[15] * tx * f * ycs  + CDT_SUB[16] * tx2 * f * ycs;
  450.             final double DTC200DZ = sum;
  451.             final double CC  = 3.0 * DTC200 - DTC200DZ;
  452.             final double DD  = DTC200 - CC;
  453.             final double ZP  = (satAlt - 120.0) / 80.0;
  454.             dTc = CC * ZP * ZP + DD * ZP * ZP * ZP;
  455.         }

  456.         if ((satAlt > 200.0) && (satAlt <= 240.0)) {
  457.             h = (satAlt - 200.0) / 50.0;
  458.             sum = CDT_SUB[1] * h + BDT_SUB[2] * f * h + CDT_SUB[3] * tx * f * h + CDT_SUB[4] * tx2 * f * h +
  459.                   CDT_SUB[5] * tx3 * f * h + CDT_SUB[6] * tx4 * f * h + CDT_SUB[7] * tx5 * f * h +
  460.                   CDT_SUB[8] * tx * ycs * h + CDT_SUB[9] * tx2 * ycs * h + CDT_SUB[10] * tx3 * ycs * h +
  461.                   CDT_SUB[11] * tx4 * ycs * h + CDT_SUB[12] * tx5 * ycs * h + CDT_SUB[13] * ycs * h +
  462.                   CDT_SUB[14] * f * ycs * h + CDT_SUB[15] * tx * f * ycs * h  + CDT_SUB[16] * tx2 * f * ycs * h +
  463.                   CDT_SUB[17] + CDT_SUB[18] * tx * ycs + CDT_SUB[19] * tx2 * ycs +
  464.                   CDT_SUB[20] * tx3 * ycs + CDT_SUB[21] * f * ycs + CDT_SUB[22] * tx * f * ycs +
  465.                   CDT_SUB[23] * tx2 * f * ycs;
  466.             dTc = sum;
  467.         }

  468.         if ((satAlt > 240.0) && (satAlt <= 300.0)) {
  469.             h = 40.0 / 50.0;
  470.             sum = CDT_SUB[1] * h + BDT_SUB[2] * f * h + CDT_SUB[3] * tx * f * h + CDT_SUB[4] * tx2 * f * h +
  471.                   CDT_SUB[5] * tx3 * f * h + CDT_SUB[6] * tx4 * f * h + CDT_SUB[7] * tx5 * f * h +
  472.                   CDT_SUB[8] * tx * ycs * h + CDT_SUB[9] * tx2 * ycs * h + CDT_SUB[10] * tx3 * ycs * h +
  473.                   CDT_SUB[11] * tx4 * ycs * h + CDT_SUB[12] * tx5 * ycs * h + CDT_SUB[13] * ycs * h +
  474.                   CDT_SUB[14] * f * ycs * h + CDT_SUB[15] * tx * f * ycs * h  + CDT_SUB[16] * tx2 * f * ycs * h +
  475.                   CDT_SUB[17] + CDT_SUB[18] * tx * ycs + CDT_SUB[19] * tx2 * ycs +
  476.                   CDT_SUB[20] * tx3 * ycs + CDT_SUB[21] * f * ycs + CDT_SUB[22] * tx * f * ycs +
  477.                   CDT_SUB[23] * tx2 * f * ycs;
  478.             final double AA = sum;
  479.             final double BB = CDT_SUB[1] + BDT_SUB[2] * f + CDT_SUB[3] * tx * f + CDT_SUB[4] * tx2 * f +
  480.                         CDT_SUB[5] * tx3 * f + CDT_SUB[6] * tx4 * f + CDT_SUB[7] * tx5 * f +
  481.                         CDT_SUB[8] * tx * ycs + CDT_SUB[9] * tx2 * ycs + CDT_SUB[10] * tx3 * ycs +
  482.                         CDT_SUB[11] * tx4 * ycs + CDT_SUB[12] * tx5 * ycs + CDT_SUB[13] * ycs +
  483.                         CDT_SUB[14] * f * ycs + CDT_SUB[15] * tx * f * ycs + CDT_SUB[16] * tx2 * f * ycs;
  484.             h   = 300.0 / 100.0;
  485.             sum = BDT_SUB[1] + BDT_SUB[2] * f  + BDT_SUB[3] * tx * f         + BDT_SUB[4] * tx2 * f +
  486.                   BDT_SUB[5] * tx3 * f      + BDT_SUB[6] * tx4 * f      + BDT_SUB[7] * tx5 * f +
  487.                   BDT_SUB[8] * tx * ycs       + BDT_SUB[9] * tx2 * ycs    + BDT_SUB[10] * tx3 * ycs +
  488.                   BDT_SUB[11] * tx4 * ycs   + BDT_SUB[12] * tx5 * ycs   + BDT_SUB[13] * h * ycs +
  489.                   BDT_SUB[14] * tx * h * ycs    + BDT_SUB[15] * tx2 * h * ycs + BDT_SUB[16] * tx3 * h * ycs +
  490.                   BDT_SUB[17] * tx4 * h * ycs + BDT_SUB[18] * tx5 * h * ycs + BDT_SUB[19] * ycs;
  491.             final double DTC300 = sum;
  492.             sum = BDT_SUB[13] * ycs +
  493.                   BDT_SUB[14] * tx * ycs + BDT_SUB[15] * tx2 * ycs + BDT_SUB[16] * tx3 * ycs +
  494.                   BDT_SUB[17] * tx4 * ycs + BDT_SUB[18] * tx5 * ycs;
  495.             final double DTC300DZ = sum;
  496.             final double CC = 3.0 * DTC300 - DTC300DZ - 3.0 * AA - 2.0 * BB;
  497.             final double  DD = DTC300 - AA - BB - CC;
  498.             final double ZP  = (satAlt - 240.0) / 60.0;
  499.             dTc = AA + BB * ZP + CC * ZP * ZP + DD * ZP * ZP * ZP;
  500.         }

  501.         if ((satAlt > 300.0) && (satAlt <= 600.0)) {
  502.             h   = satAlt / 100.0;
  503.             sum = BDT_SUB[1]    + BDT_SUB[2] * f  + BDT_SUB[3] * tx * f         + BDT_SUB[4] * tx2 * f +
  504.                   BDT_SUB[5] * tx3 * f      + BDT_SUB[6] * tx4 * f      + BDT_SUB[7] * tx5 * f +
  505.                   BDT_SUB[8] * tx * ycs       + BDT_SUB[9] * tx2 * ycs    + BDT_SUB[10] * tx3 * ycs +
  506.                   BDT_SUB[11] * tx4 * ycs   + BDT_SUB[12] * tx5 * ycs   + BDT_SUB[13] * h * ycs +
  507.                   BDT_SUB[14] * tx * h * ycs    + BDT_SUB[15] * tx2 * h * ycs + BDT_SUB[16] * tx3 * h * ycs +
  508.                   BDT_SUB[17] * tx4 * h * ycs + BDT_SUB[18] * tx5 * h * ycs + BDT_SUB[19] * ycs;
  509.             dTc = sum;
  510.         }

  511.         if ((satAlt > 600.0) && (satAlt <= 800.0)) {
  512.             final double ZP = (satAlt - 600.0) / 100.0;
  513.             final double HP = 600.0 / 100.0;
  514.             final double AA  = BDT_SUB[1]    + BDT_SUB[2] * f  + BDT_SUB[3] * tx * f         + BDT_SUB[4] * tx2 * f +
  515.                                BDT_SUB[5] * tx3 * f      + BDT_SUB[6] * tx4 * f      + BDT_SUB[7] * tx5 * f +
  516.                                BDT_SUB[8] * tx * ycs       + BDT_SUB[9] * tx2 * ycs    + BDT_SUB[10] * tx3 * ycs +
  517.                                BDT_SUB[11] * tx4 * ycs   + BDT_SUB[12] * tx5 * ycs   + BDT_SUB[13] * HP * ycs +
  518.                                BDT_SUB[14] * tx * HP * ycs   + BDT_SUB[15] * tx2 * HP * ycs + BDT_SUB[16] * tx3 * HP * ycs +
  519.                                BDT_SUB[17] * tx4 * HP * ycs + BDT_SUB[18] * tx5 * HP * ycs + BDT_SUB[19] * ycs;
  520.             final double BB  = BDT_SUB[13] * ycs +
  521.                                BDT_SUB[14] * tx * ycs    + BDT_SUB[15] * tx2 * ycs + BDT_SUB[16] * tx3 * ycs +
  522.                                BDT_SUB[17] * tx4 * ycs + BDT_SUB[18] * tx5 * ycs;
  523.             final double CC  = -(3.0 * AA + 4.0 * BB) / 4.0;
  524.             final double DD  = (AA + BB) / 4.0;
  525.             dTc = AA + BB * ZP + CC * ZP * ZP + DD * ZP * ZP * ZP;
  526.         }

  527.         return dTc;
  528.     }

  529.     /** Evaluates Equation (1).
  530.      * @param z altitude
  531.      * @return equation (1) value
  532.      */
  533.     private static double xAmbar(final double z) {
  534.         final double dz = z - 100.;
  535.         double amb = CXAMB[7];
  536.         for (int i = 6; i >= 1; --i) {
  537.             amb = dz * amb + CXAMB[i];
  538.         }
  539.         return amb;
  540.     }

  541.     /**  Evaluates Equation (10) or Equation (13), depending on Z.
  542.      * @param z altitude
  543.      * @param TC tc array ???
  544.      * @return equation (10) value
  545.      */
  546.     private static double xLocal(final double z, final double[] TC) {
  547.         final double dz = z - 125;
  548.         if (dz <= 0) {
  549.             return ((-9.8204695e-6 * dz - 7.3039742e-4) * dz * dz + 1.0) * dz * TC[1] + TC[0];
  550.         } else {
  551.             return TC[0] + TC[2] * FastMath.atan(TC[3] * dz * (1 + 4.5e-6 * FastMath.pow(dz, 2.5)));
  552.         }
  553.     }

  554.     /** Evaluates Equation (8) of gravity field.
  555.      * @param z altitude
  556.      * @return the gravity field
  557.      */
  558.     private static double xGrav(final double z) {
  559.         final double temp = 1.0 + z / 6356.766;
  560.         return 9.80665 / (temp * temp);
  561.     }

  562.     /** Compute semi-annual variation (delta log(rho)).
  563.      * @param day day of year
  564.      * @param height height (km)
  565.      * @param f10Bar average 81-day centered f10
  566.      * @return semi-annual variation
  567.      */
  568.     private static double semian (final double day, final double height, final double f10Bar) {

  569.         final double f10Bar2 = f10Bar * f10Bar;
  570.         final double htz = height / 1000.0;

  571.         // SEMIANNUAL AMPLITUDE
  572.         final double fzz = FZM[1] + FZM[2] * f10Bar  + FZM[3] * f10Bar * htz +
  573.                            FZM[4] * f10Bar * htz * htz + FZM[5] * f10Bar * f10Bar * htz +
  574.                            FZM[6] * f10Bar * f10Bar * htz * htz;

  575.         // SEMIANNUAL PHASE FUNCTION
  576.         final double tau   = TWOPI * (day - 1.0) / 365;
  577.         final double sin1P = FastMath.sin(tau);
  578.         final double cos1P = FastMath.cos(tau);
  579.         final double sin2P = FastMath.sin(2.0 * tau);
  580.         final double cos2P = FastMath.cos(2.0 * tau);
  581.         final double sin3P = FastMath.sin(3.0 * tau);
  582.         final double cos3P = FastMath.cos(3.0 * tau);
  583.         final double sin4P = FastMath.sin(4.0 * tau);
  584.         final double cos4P = FastMath.cos(4.0 * tau);
  585.         final double gtz = GTM[1] + GTM[2] * sin1P + GTM[3] * cos1P +
  586.                            GTM[4] * sin2P + GTM[5] * cos2P +
  587.                            GTM[6] * sin3P + GTM[7] * cos3P +
  588.                            GTM[8] * sin4P + GTM[9] * cos4P +
  589.                            GTM[10] * f10Bar + GTM[11] * f10Bar * sin1P + GTM[12] * f10Bar * cos1P +
  590.                            GTM[13] * f10Bar * sin2P + GTM[14] * f10Bar * cos2P +
  591.                            GTM[15] * f10Bar * sin3P + GTM[16] * f10Bar * cos3P +
  592.                            GTM[17] * f10Bar * sin4P + GTM[18] * f10Bar * cos4P +
  593.                            GTM[19] * f10Bar2  + GTM[20] * f10Bar2  * sin1P + GTM[21] * f10Bar2  * cos1P +
  594.                            GTM[22] * f10Bar2  * sin2P + GTM[23] * f10Bar2  * cos2P;

  595.         return FastMath.max(1.0e-6, fzz) * gtz;

  596.     }

  597.     /** Compute day of year.
  598.      * @param d1950 (days since 1950)
  599.      * @return the number days in year
  600.      */
  601.     private static double dayOfYear(final double d1950) {

  602.         int iyday = (int) d1950;
  603.         final double frac = d1950 - iyday;
  604.         iyday = iyday + 364;

  605.         int itemp = iyday / 1461;

  606.         iyday = iyday - itemp * 1461;
  607.         itemp = iyday / 365;
  608.         if (itemp >= 3) {
  609.             itemp = 3;
  610.         }
  611.         iyday = iyday - 365 * itemp + 1;
  612.         return iyday + frac;
  613.     }

  614.     // OUTPUT:

  615.     /** Get the exospheric temperature above input position.
  616.      * {@link #getDensity(double, double, double, double, double, double, double, double, double, double, double, double, double)}
  617.      * <b> must </b> must be called before calling this function.
  618.      * @return the exospheric temperature (deg K)
  619.      */
  620.     public double getExosphericTemp() {
  621.         return temp[1];
  622.     }

  623.     /** Get the temperature at input position.
  624.      * {@link #getDensity(double, double, double, double, double, double, double, double, double, double, double, double, double)}
  625.      * <b> must </b> must be called before calling this function.
  626.      * @return the local temperature (deg K)
  627.      */
  628.     public double getLocalTemp() {
  629.         return temp[2];
  630.     }

  631.     /** Get the local density.
  632.      * @param date current date
  633.      * @param position current position in frame
  634.      * @param frame the frame in which is defined the position
  635.      * @return local density (kg/m<sup>3</sup>)
  636.      * @exception OrekitException if date is out of range of solar activity
  637.      */
  638.     public double getDensity(final AbsoluteDate date, final Vector3D position,
  639.                              final Frame frame)
  640.         throws OrekitException {
  641.         // check if data are available :
  642.         if (date.compareTo(inputParams.getMaxDate()) > 0 ||
  643.             date.compareTo(inputParams.getMinDate()) < 0) {
  644.             throw new OrekitException(OrekitMessages.NO_SOLAR_ACTIVITY_AT_DATE,
  645.                                       date, inputParams.getMinDate(), inputParams.getMaxDate());
  646.         }

  647.         // compute modified julian days date
  648.         final double dateMJD = date.durationFrom(AbsoluteDate.MODIFIED_JULIAN_EPOCH) / Constants.JULIAN_DAY;

  649.         // compute geodetic position
  650.         final GeodeticPoint inBody = earth.transform(position, frame, date);

  651.         // compute sun position
  652.         final GeodeticPoint sunInBody =
  653.             earth.transform(sun.getPVCoordinates(date, frame).getPosition(), frame, date);
  654.         return getDensity(dateMJD,
  655.                           sunInBody.getLongitude(), sunInBody.getLatitude(),
  656.                           inBody.getLongitude(), inBody.getLatitude(),
  657.                           inBody.getAltitude(), inputParams.getF10(date),
  658.                           inputParams.getF10B(date),
  659.                           inputParams.getAp(date), inputParams.getS10(date),
  660.                           inputParams.getS10B(date), inputParams.getXM10(date),
  661.                           inputParams.getXM10B(date));
  662.     }

  663.     /** Get the inertial velocity of atmosphere molecules.
  664.      * Here the case is simplified : atmosphere is supposed to have a null velocity
  665.      * in earth frame.
  666.      * @param date current date
  667.      * @param position current position in frame
  668.      * @param frame the frame in which is defined the position
  669.      * @return velocity (m/s) (defined in the same frame as the position)
  670.      * @exception OrekitException if some frame conversion cannot be performed
  671.      */
  672.     public Vector3D getVelocity(final AbsoluteDate date, final Vector3D position,
  673.                                 final Frame frame)
  674.         throws OrekitException {
  675.         final Transform bodyToFrame = earth.getBodyFrame().getTransformTo(frame, date);
  676.         final Vector3D posInBody = bodyToFrame.getInverse().transformPosition(position);
  677.         final PVCoordinates pvBody = new PVCoordinates(posInBody, new Vector3D(0, 0, 0));
  678.         final PVCoordinates pvFrame = bodyToFrame.transformPVCoordinates(pvBody);
  679.         return pvFrame.getVelocity();
  680.     }

  681. }