JB2008.java
- /* Copyright 2002-2025 CS GROUP
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.models.earth.atmosphere;
- import org.hipparchus.Field;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
- import org.hipparchus.geometry.euclidean.threed.Vector3D;
- import org.hipparchus.util.FastMath;
- import org.hipparchus.util.FieldSinCos;
- import org.hipparchus.util.MathArrays;
- import org.hipparchus.util.MathUtils;
- import org.hipparchus.util.SinCos;
- import org.orekit.annotation.DefaultDataContext;
- import org.orekit.bodies.BodyShape;
- import org.orekit.bodies.FieldGeodeticPoint;
- import org.orekit.bodies.GeodeticPoint;
- import org.orekit.data.DataContext;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.frames.Frame;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.time.DateTimeComponents;
- import org.orekit.time.FieldAbsoluteDate;
- import org.orekit.time.TimeScale;
- import org.orekit.utils.Constants;
- import org.orekit.utils.ExtendedPositionProvider;
- /** This is the realization of the Jacchia-Bowman 2008 atmospheric model.
- * <p>
- * It is described in the paper:<br>
- * <a href="https://www.researchgate.net/publication/228621668_A_New_Empirical_Thermospheric_Density_Model_JB2008_Using_New_Solar_and_Geomagnetic_Indices">A
- * New Empirical Thermospheric Density Model JB2008 Using New Solar Indices</a><br>
- * <i>Bruce R. Bowman & al.</i><br>
- * AIAA 2008-6438<br>
- * </p>
- * <p>
- * Two computation methods are proposed to the user:
- * <ul>
- * <li>one OREKIT independent and compliant with initial FORTRAN routine entry values:
- * {@link #getDensity(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)}. </li>
- * <li>one compliant with OREKIT Atmosphere interface, necessary to the
- * {@link org.orekit.forces.drag.DragForce drag force model} computation.</li>
- * </ul>
- * <p>
- * This model provides dense output for all altitudes and positions. Output data are :
- * <ul>
- * <li>Exospheric Temperature above Input Position (deg K)</li>
- * <li>Temperature at Input Position (deg K)</li>
- * <li>Total Mass-Density at Input Position (kg/m³)</li>
- * </ul>
- * <p>
- * The model needs geographical and time information to compute general values,
- * but also needs space weather data : mean and daily solar flux, retrieved through
- * different indices, and planetary geomagnetic indices.<br>
- * More information on these indices can be found on the <a
- * href="http://sol.spacenvironment.net/~JB2008/indices.html">
- * official JB2008 website.</a>
- * </p>
- *
- * @author Bruce R Bowman (HQ AFSPC, Space Analysis Division), 2008: FORTRAN routine
- * @author Pascal Parraud (java translation)
- */
- public class JB2008 extends AbstractSunInfluencedAtmosphere {
- /** Minimum altitude (m) for JB2008 use. */
- private static final double ALT_MIN = 90000.;
- /** Earth radius (km). */
- private static final double EARTH_RADIUS = 6356.766;
- /** Natural logarithm of 10.0. */
- private static final double LOG10 = FastMath.log(10.);
- /** Avogadro's number in mks units (molecules/kmol). */
- private static final double AVOGAD = 6.02257e26;
- /** Universal gas-constant in mks units (joules/K/kmol). */
- private static final double RSTAR = 8.31432;
- /** The alpha are the thermal diffusion coefficients in Equation (6). */
- private static final double[] ALPHA = {
- 0, 0, 0, 0, -0.38
- };
- /** Molecular weights in order: N2, O2, O, Ar, He and H. */
- private static final double[] AMW = {
- 28.0134, 31.9988, 15.9994, 39.9480, 4.0026, 1.00797
- };
- /** The FRAC are the assumed sea-level volume fractions in order: N2, O2, Ar, and He. */
- private static final double[] FRAC = {
- 0.78110, 0.20955, 9.3400e-3, 1.2890e-5
- };
- /** Value used to establish height step sizes in the regime 90km to 105km. */
- private static final double R1 = 0.010;
- /** Value used to establish height step sizes in the regime 105km to 500km. */
- private static final double R2 = 0.025;
- /** Value used to establish height step sizes in the regime above 500km. */
- private static final double R3 = 0.075;
- /** Weights for the Newton-Cotes five-points quadrature formula. */
- private static final double[] WT = {
- 0.311111111111111, 1.422222222222222, 0.533333333333333, 1.422222222222222, 0.311111111111111
- };
- /** Coefficients for high altitude density correction. */
- private static final double[] CHT = {
- 0.22, -0.20e-02, 0.115e-02, -0.211e-05
- };
- /** FZ global model values (1997-2006 fit). */
- private static final double[] FZM = {
- 0.2689e+00, -0.1176e-01, 0.2782e-01, -0.2782e-01, 0.3470e-03
- };
- /** GT global model values (1997-2006 fit). */
- private static final double[] GTM = {
- -0.3633e+00, 0.8506e-01, 0.2401e+00, -0.1897e+00, -0.2554e+00,
- -0.1790e-01, 0.5650e-03, -0.6407e-03, -0.3418e-02, -0.1252e-02
- };
- /** Mbar polynomial coeffcients. */
- private static final double[] CMB = {
- 28.15204, -8.5586e-2, +1.2840e-4, -1.0056e-5, -1.0210e-5, +1.5044e-6, +9.9826e-8
- };
- /** DTC relative data. */
- private static final double[] BDTC = {
- -0.457512297e+01, -0.512114909e+01, -0.693003609e+02,
- 0.203716701e+03, 0.703316291e+03, -0.194349234e+04,
- 0.110651308e+04, -0.174378996e+03, 0.188594601e+04,
- -0.709371517e+04, 0.922454523e+04, -0.384508073e+04,
- -0.645841789e+01, 0.409703319e+02, -0.482006560e+03,
- 0.181870931e+04, -0.237389204e+04, 0.996703815e+03,
- 0.361416936e+02
- };
- /** DTC relative data. */
- private static final double[] CDTC = {
- -0.155986211e+02, -0.512114909e+01, -0.693003609e+02,
- 0.203716701e+03, 0.703316291e+03, -0.194349234e+04,
- 0.110651308e+04, -0.220835117e+03, 0.143256989e+04,
- -0.318481844e+04, 0.328981513e+04, -0.135332119e+04,
- 0.199956489e+02, -0.127093998e+02, 0.212825156e+02,
- -0.275555432e+01, 0.110234982e+02, 0.148881951e+03,
- -0.751640284e+03, 0.637876542e+03, 0.127093998e+02,
- -0.212825156e+02, 0.275555432e+01
- };
- /** External data container. */
- private JB2008InputParameters inputParams;
- /** Earth body shape. */
- private final BodyShape earth;
- /** UTC time scale. */
- private final TimeScale utc;
- /** Constructor with space environment information for internal computation.
- *
- * <p>This method uses the {@link DataContext#getDefault() default data context}.
- *
- * @param parameters the solar and magnetic activity data
- * @param sun the sun position
- * @param earth the earth body shape
- * @see #JB2008(JB2008InputParameters, ExtendedPositionProvider, BodyShape, TimeScale)
- */
- @DefaultDataContext
- public JB2008(final JB2008InputParameters parameters,
- final ExtendedPositionProvider sun, final BodyShape earth) {
- this(parameters, sun, earth, DataContext.getDefault().getTimeScales().getUTC());
- }
- /**
- * Constructor with space environment information for internal computation.
- *
- * @param parameters the solar and magnetic activity data
- * @param sun the sun position
- * @param earth the earth body shape
- * @param utc UTC time scale. Used to computed the day fraction.
- * @since 10.1
- */
- public JB2008(final JB2008InputParameters parameters,
- final ExtendedPositionProvider sun,
- final BodyShape earth,
- final TimeScale utc) {
- super(sun);
- this.earth = earth;
- this.inputParams = parameters;
- this.utc = utc;
- }
- /** {@inheritDoc} */
- public Frame getFrame() {
- return earth.getBodyFrame();
- }
- /** Get the local density with initial entries.
- * @param dateMJD date and time, in modified julian days and fraction
- * @param sunRA Right Ascension of Sun (radians)
- * @param sunDecli Declination of Sun (radians)
- * @param satLon Right Ascension of position (radians)
- * @param satLat Geocentric latitude of position (radians)
- * @param satAlt Height of position (m)
- * @param f10 10.7-cm Solar flux (1e<sup>-22</sup>*Watt/(m²*Hertz))<br>
- * (Tabular time 1.0 day earlier)
- * @param f10B 10.7-cm Solar Flux, averaged 81-day centered on the input time<br>
- * (Tabular time 1.0 day earlier)
- * @param s10 EUV index (26-34 nm) scaled to F10<br>
- * (Tabular time 1 day earlier)
- * @param s10B UV 81-day averaged centered index
- * (Tabular time 1 day earlier)
- * @param xm10 MG2 index scaled to F10<br>
- * (Tabular time 2.0 days earlier)
- * @param xm10B MG2 81-day ave. centered index<br>
- * (Tabular time 2.0 days earlier)
- * @param y10 Solar X-Ray & Lya index scaled to F10<br>
- * (Tabular time 5.0 days earlier)
- * @param y10B Solar X-Ray & Lya 81-day ave. centered index<br>
- * (Tabular time 5.0 days earlier)
- * @param dstdtc Temperature change computed from Dst index
- * @return total mass-Density at input position (kg/m³)
- */
- public double getDensity(final double dateMJD, final double sunRA, final double sunDecli,
- final double satLon, final double satLat, final double satAlt,
- final double f10, final double f10B, final double s10,
- final double s10B, final double xm10, final double xm10B,
- final double y10, final double y10B, final double dstdtc) {
- if (satAlt < ALT_MIN) {
- throw new OrekitException(OrekitMessages.ALTITUDE_BELOW_ALLOWED_THRESHOLD, satAlt, ALT_MIN);
- }
- final double altKm = satAlt / 1000.0;
- // Equation (14)
- final double fn = FastMath.min(1.0, FastMath.pow(f10B / 240., 0.25));
- final double fsb = f10B * fn + s10B * (1. - fn);
- final double tsubc = 392.4 + 3.227 * fsb + 0.298 * (f10 - f10B) + 2.259 * (s10 - s10B) +
- 0.312 * (xm10 - xm10B) + 0.178 * (y10 - y10B);
- // Equation (15)
- final double eta = 0.5 * FastMath.abs(satLat - sunDecli);
- final double theta = 0.5 * FastMath.abs(satLat + sunDecli);
- // Equation (16)
- final double h = satLon - sunRA;
- final double tau = h - 0.64577182 + 0.10471976 * FastMath.sin(h + 0.75049158);
- double solarTime = FastMath.toDegrees(h + FastMath.PI) / 15.0;
- while (solarTime >= 24) {
- solarTime -= 24.;
- }
- while (solarTime < 0) {
- solarTime += 24.;
- }
- // Equation (17)
- final double cosEta = FastMath.pow(FastMath.cos(eta), 2.5);
- final double sinTeta = FastMath.pow(FastMath.sin(theta), 2.5);
- final double cosTau = FastMath.abs(FastMath.cos(0.5 * tau));
- final double df = sinTeta + (cosEta - sinTeta) * cosTau * cosTau * cosTau;
- final double tsubl = tsubc * (1. + 0.31 * df);
- // Compute correction to dTc for local solar time and lat correction
- final double dtclst = dTc(f10, solarTime, satLat, altKm);
- // Compute the local exospheric temperature.
- // Add geomagnetic storm effect from input dTc value
- final double[] temp = new double[2];
- temp[0] = tsubl + dstdtc;
- final double tinf = temp[0] + dtclst;
- // Equation (9)
- final double tsubx = 444.3807 + 0.02385 * tinf - 392.8292 * FastMath.exp(-0.0021357 * tinf);
- // Equation (11)
- final double gsubx = 0.054285714 * (tsubx - 183.);
- // The TC array will be an argument in the call to localTemp,
- // which evaluates Eq. (10) or (13)
- final double[] tc = new double[4];
- tc[0] = tsubx;
- tc[1] = gsubx;
- // A of Equation (13)
- tc[2] = (tinf - tsubx) * 2. / FastMath.PI;
- tc[3] = gsubx / tc[2];
- // Equation (5)
- final double z1 = 90.;
- final double z2 = FastMath.min(altKm, 105.0);
- double al = FastMath.log(z2 / z1);
- int n = 1 + (int) FastMath.floor(al / R1);
- double zr = FastMath.exp(al / n);
- final double mb1 = mBar(z1);
- final double tloc1 = localTemp(z1, tc);
- double zend = z1;
- double sub2 = 0.;
- double ain = mb1 * gravity(z1) / tloc1;
- double mb2 = 0;
- double tloc2 = 0;
- double z = 0;
- double gravl = 0;
- for (int i = 0; i < n; ++i) {
- z = zend;
- zend = zr * z;
- final double dz = 0.25 * (zend - z);
- double sum1 = WT[0] * ain;
- for (int j = 1; j < 5; ++j) {
- z += dz;
- mb2 = mBar(z);
- tloc2 = localTemp(z, tc);
- gravl = gravity(z);
- ain = mb2 * gravl / tloc2;
- sum1 += WT[j] * ain;
- }
- sub2 += dz * sum1;
- }
- double rho = 3.46e-6 * mb2 * tloc1 / FastMath.exp(sub2 / RSTAR) / (mb1 * tloc2);
- // Equation (2)
- final double anm = AVOGAD * rho;
- final double an = anm / mb2;
- // Equation (3)
- double fact2 = anm / 28.960;
- final double[] aln = new double[6];
- aln[0] = FastMath.log(FRAC[0] * fact2);
- aln[3] = FastMath.log(FRAC[2] * fact2);
- aln[4] = FastMath.log(FRAC[3] * fact2);
- // Equation (4)
- aln[1] = FastMath.log(fact2 * (1. + FRAC[1]) - an);
- aln[2] = FastMath.log(2. * (an - fact2));
- if (altKm <= 105.0) {
- temp[1] = tloc2;
- // Put in negligible hydrogen for use in DO-LOOP 13
- aln[5] = aln[4] - 25.0;
- } else {
- // Equation (6)
- al = FastMath.log(FastMath.min(altKm, 500.0) / z);
- n = 1 + (int) FastMath.floor(al / R2);
- zr = FastMath.exp(al / n);
- sub2 = 0.;
- ain = gravl / tloc2;
- double tloc3 = 0;
- for (int i = 0; i < n; ++i) {
- z = zend;
- zend = zr * z;
- final double dz = 0.25 * (zend - z);
- double sum1 = WT[0] * ain;
- for (int j = 1; j < 5; ++j) {
- z += dz;
- tloc3 = localTemp(z, tc);
- gravl = gravity(z);
- ain = gravl / tloc3;
- sum1 += WT[j] * ain;
- }
- sub2 += dz * sum1;
- }
- al = FastMath.log(FastMath.max(altKm, 500.0) / z);
- final double r = (altKm > 500.0) ? R3 : R2;
- n = 1 + (int) FastMath.floor(al / r);
- zr = FastMath.exp(al / n);
- double sum3 = 0.;
- double tloc4 = 0;
- for (int i = 0; i < n; ++i) {
- z = zend;
- zend = zr * z;
- final double dz = 0.25 * (zend - z);
- double sum1 = WT[0] * ain;
- for (int j = 1; j < 5; ++j) {
- z += dz;
- tloc4 = localTemp(z, tc);
- gravl = gravity(z);
- ain = gravl / tloc4;
- sum1 += WT[j] * ain;
- }
- sum3 += dz * sum1;
- }
- final double altr;
- final double hSign;
- if (altKm <= 500.) {
- temp[1] = tloc3;
- altr = FastMath.log(tloc3 / tloc2);
- fact2 = sub2 / RSTAR;
- hSign = 1.0;
- } else {
- temp[1] = tloc4;
- altr = FastMath.log(tloc4 / tloc2);
- fact2 = (sub2 + sum3) / RSTAR;
- hSign = -1.0;
- }
- for (int i = 0; i < 5; ++i) {
- aln[i] = aln[i] - (1.0 + ALPHA[i]) * altr - fact2 * AMW[i];
- }
- // Equation (7)
- final double al10t5 = FastMath.log10(tinf);
- final double alnh5 = (5.5 * al10t5 - 39.40) * al10t5 + 73.13;
- aln[5] = LOG10 * (alnh5 + 6.) + hSign * (FastMath.log(tloc4 / tloc3) + sum3 * AMW[5] / RSTAR);
- }
- // Equation (24) - J70 Seasonal-Latitudinal Variation
- final double capPhi = ((dateMJD - 36204.0) / 365.2422) % 1;
- final int signum = (satLat >= 0.) ? 1 : -1;
- final double sinLat = FastMath.sin(satLat);
- final double hm90 = altKm - 90.;
- final double dlrsl = 0.02 * hm90 * FastMath.exp(-0.045 * hm90) *
- signum * FastMath.sin(MathUtils.TWO_PI * capPhi + 1.72) * sinLat * sinLat;
- // Equation (23) - Computes the semiannual variation
- double dlrsa = 0;
- if (z < 2000.0) {
- // Use new semiannual model dLog(rho)
- dlrsa = semian08(dayOfYear(dateMJD), altKm, f10B, s10B, xm10B);
- }
- // Sum the delta-log-rhos and apply to the number densities.
- // In CIRA72 the following equation contains an actual sum,
- // namely DLR = LOG10 * (DLRGM + DLRSA + DLRSL)
- // However, for Jacchia 70, there is no DLRGM or DLRSA.
- final double dlr = LOG10 * (dlrsl + dlrsa);
- for (int i = 0; i < 6; ++i) {
- aln[i] += dlr;
- }
- // Compute mass-density and mean-molecular-weight and
- // convert number density logs from natural to common.
- double sumnm = 0.0;
- for (int i = 0; i < 6; ++i) {
- sumnm += FastMath.exp(aln[i]) * AMW[i];
- }
- rho = sumnm / AVOGAD;
- // Compute the high altitude exospheric density correction factor
- double fex = 1.0;
- if (altKm >= 1000.0 && altKm < 1500.0) {
- final double zeta = (altKm - 1000.) * 0.002;
- final double f15c = CHT[0] + CHT[1] * f10B + (CHT[2] + CHT[3] * f10B) * 1500.0;
- final double f15cZeta = (CHT[2] + CHT[3] * f10B) * 500.0;
- final double fex2 = 3.0 * f15c - f15cZeta - 3.0;
- final double fex3 = f15cZeta - 2.0 * f15c + 2.0;
- fex += zeta * zeta * (fex2 + fex3 * zeta);
- } else if (altKm >= 1500.0) {
- fex = CHT[0] + CHT[1] * f10B + CHT[2] * altKm + CHT[3] * f10B * altKm;
- }
- // Apply the exospheric density correction factor.
- rho *= fex;
- return rho;
- }
- /** Get the local density with initial entries.
- * @param dateMJD date and time, in modified julian days and fraction
- * @param sunRA Right Ascension of Sun (radians)
- * @param sunDecli Declination of Sun (radians)
- * @param satLon Right Ascension of position (radians)
- * @param satLat Geocentric latitude of position (radians)
- * @param satAlt Height of position (m)
- * @param f10 10.7-cm Solar flux (1e<sup>-22</sup>*Watt/(m²*Hertz))<br>
- * (Tabular time 1.0 day earlier)
- * @param f10B 10.7-cm Solar Flux, averaged 81-day centered on the input time<br>
- * (Tabular time 1.0 day earlier)
- * @param s10 EUV index (26-34 nm) scaled to F10<br>
- * (Tabular time 1 day earlier)
- * @param s10B UV 81-day averaged centered index
- * (Tabular time 1 day earlier)
- * @param xm10 MG2 index scaled to F10<br>
- * (Tabular time 2.0 days earlier)
- * @param xm10B MG2 81-day ave. centered index<br>
- * (Tabular time 2.0 days earlier)
- * @param y10 Solar X-Ray & Lya index scaled to F10<br>
- * (Tabular time 5.0 days earlier)
- * @param y10B Solar X-Ray & Lya 81-day ave. centered index<br>
- * (Tabular time 5.0 days earlier)
- * @param dstdtc Temperature change computed from Dst index
- * @param <T> type of the field elements
- * @return total mass-Density at input position (kg/m³)
- */
- public <T extends CalculusFieldElement<T>> T getDensity(final T dateMJD, final T sunRA, final T sunDecli,
- final T satLon, final T satLat, final T satAlt,
- final double f10, final double f10B, final double s10,
- final double s10B, final double xm10, final double xm10B,
- final double y10, final double y10B, final double dstdtc) {
- if (satAlt.getReal() < ALT_MIN) {
- throw new OrekitException(OrekitMessages.ALTITUDE_BELOW_ALLOWED_THRESHOLD,
- satAlt.getReal(), ALT_MIN);
- }
- final Field<T> field = satAlt.getField();
- final T pi = field.getOne().getPi();
- final T altKm = satAlt.divide(1000.0);
- // Equation (14)
- final double fn = FastMath.min(1.0, FastMath.pow(f10B / 240., 0.25));
- final double fsb = f10B * fn + s10B * (1. - fn);
- final double tsubc = 392.4 + 3.227 * fsb + 0.298 * (f10 - f10B) + 2.259 * (s10 - s10B) +
- 0.312 * (xm10 - xm10B) + 0.178 * (y10 - y10B);
- // Equation (15)
- final T eta = satLat.subtract(sunDecli).abs().multiply(0.5);
- final T theta = satLat.add(sunDecli).abs().multiply(0.5);
- // Equation (16)
- final T h = satLon.subtract(sunRA);
- final T tau = h.subtract(0.64577182).add(h.add(0.75049158).sin().multiply(0.10471976));
- T solarTime = FastMath.toDegrees(h.add(pi)).divide(15.0);
- while (solarTime.getReal() >= 24) {
- solarTime = solarTime.subtract(24);
- }
- while (solarTime.getReal() < 0) {
- solarTime = solarTime.add(24);
- }
- // Equation (17)
- final T cos = eta.cos();
- final T cosEta = cos.square().multiply(cos.sqrt());
- final T sin = theta.sin();
- final T sinTeta = sin.square().multiply(sin.sqrt());
- final T cosTau = tau.multiply(0.5).cos().abs();
- final T df = sinTeta.add(cosEta.subtract(sinTeta).multiply(cosTau).multiply(cosTau).multiply(cosTau));
- final T tsubl = df.multiply(0.31).add(1).multiply(tsubc);
- // Compute correction to dTc for local solar time and lat correction
- final T dtclst = dTc(f10, solarTime, satLat, altKm);
- // Compute the local exospheric temperature.
- // Add geomagnetic storm effect from input dTc value
- final T[] temp = MathArrays.buildArray(field, 2);
- temp[0] = tsubl.add(dstdtc);
- final T tinf = temp[0].add(dtclst);
- // Equation (9)
- final T tsubx = tinf.multiply(0.02385).add(444.3807).subtract(tinf.multiply(-0.0021357).exp().multiply(392.8292));
- // Equation (11)
- final T gsubx = tsubx.subtract(183.).multiply(0.054285714);
- // The TC array will be an argument in the call to localTemp,
- // which evaluates Eq. (10) or (13)
- final T[] tc = MathArrays.buildArray(field, 4);
- tc[0] = tsubx;
- tc[1] = gsubx;
- // A of Equation (13)
- tc[2] = tinf.subtract(tsubx).multiply(pi.reciprocal().multiply(2.0));
- tc[3] = gsubx.divide(tc[2]);
- // Equation (5)
- final T z1 = field.getZero().newInstance(90.);
- final T z2 = min(105.0, altKm);
- T al = z2.divide(z1).log();
- int n = 1 + (int) FastMath.floor(al.getReal() / R1);
- T zr = al.divide(n).exp();
- final T mb1 = mBar(z1);
- final T tloc1 = localTemp(z1, tc);
- T zend = z1;
- T sub2 = field.getZero();
- T ain = mb1.multiply(gravity(z1)).divide(tloc1);
- T mb2 = field.getZero();
- T tloc2 = field.getZero();
- T z = field.getZero();
- T gravl = field.getZero();
- for (int i = 0; i < n; ++i) {
- z = zend;
- zend = zr.multiply(z);
- final T dz = zend.subtract(z).multiply(0.25);
- T sum1 = ain.multiply(WT[0]);
- for (int j = 1; j < 5; ++j) {
- z = z.add(dz);
- mb2 = mBar(z);
- tloc2 = localTemp(z, tc);
- gravl = gravity(z);
- ain = mb2.multiply(gravl).divide(tloc2);
- sum1 = sum1.add(ain.multiply(WT[j]));
- }
- sub2 = sub2.add(dz.multiply(sum1));
- }
- T rho = mb2.multiply(3.46e-6).multiply(tloc1).divide(sub2.divide(RSTAR).exp().multiply(mb1.multiply(tloc2)));
- // Equation (2)
- final T anm = rho.multiply(AVOGAD);
- final T an = anm.divide(mb2);
- // Equation (3)
- T fact2 = anm.divide(28.960);
- final T[] aln = MathArrays.buildArray(field, 6);
- aln[0] = fact2.multiply(FRAC[0]).log();
- aln[3] = fact2.multiply(FRAC[2]).log();
- aln[4] = fact2.multiply(FRAC[3]).log();
- // Equation (4)
- aln[1] = fact2.multiply(1. + FRAC[1]).subtract(an).log();
- aln[2] = an.subtract(fact2).multiply(2).log();
- if (altKm.getReal() <= 105.0) {
- temp[1] = tloc2;
- // Put in negligible hydrogen for use in DO-LOOP 13
- aln[5] = aln[4].subtract(25.0);
- } else {
- // Equation (6)
- al = min(500.0, altKm).divide(z).log();
- n = 1 + (int) FastMath.floor(al.getReal() / R2);
- zr = al.divide(n).exp();
- sub2 = field.getZero();
- ain = gravl.divide(tloc2);
- T tloc3 = field.getZero();
- for (int i = 0; i < n; ++i) {
- z = zend;
- zend = zr.multiply(z);
- final T dz = zend.subtract(z).multiply(0.25);
- T sum1 = ain.multiply(WT[0]);
- for (int j = 1; j < 5; ++j) {
- z = z.add(dz);
- tloc3 = localTemp(z, tc);
- gravl = gravity(z);
- ain = gravl.divide(tloc3);
- sum1 = sum1.add(ain.multiply(WT[j]));
- }
- sub2 = sub2.add(dz.multiply(sum1));
- }
- al = max(500.0, altKm).divide(z).log();
- final double r = (altKm.getReal() > 500.0) ? R3 : R2;
- n = 1 + (int) FastMath.floor(al.getReal() / r);
- zr = al.divide(n).exp();
- T sum3 = field.getZero();
- T tloc4 = field.getZero();
- for (int i = 0; i < n; ++i) {
- z = zend;
- zend = zr.multiply(z);
- final T dz = zend.subtract(z).multiply(0.25);
- T sum1 = ain.multiply(WT[0]);
- for (int j = 1; j < 5; ++j) {
- z = z.add(dz);
- tloc4 = localTemp(z, tc);
- gravl = gravity(z);
- ain = gravl.divide(tloc4);
- sum1 = sum1.add(ain.multiply(WT[j]));
- }
- sum3 = sum3.add(dz.multiply(sum1));
- }
- final T altr;
- final double hSign;
- if (altKm.getReal() <= 500.) {
- temp[1] = tloc3;
- altr = tloc3.divide(tloc2).log();
- fact2 = sub2.divide(RSTAR);
- hSign = 1.0;
- } else {
- temp[1] = tloc4;
- altr = tloc4.divide(tloc2).log();
- fact2 = sub2.add(sum3).divide(RSTAR);
- hSign = -1.0;
- }
- for (int i = 0; i < 5; ++i) {
- aln[i] = aln[i].subtract(altr.multiply(1.0 + ALPHA[i])).subtract(fact2.multiply(AMW[i]));
- }
- // Equation (7)
- final T al10t5 = tinf.log10();
- final T alnh5 = al10t5.multiply(5.5).subtract(39.40).multiply(al10t5).add(73.13);
- aln[5] = alnh5.add(6.).multiply(LOG10).
- add(tloc4.divide(tloc3).log().add(sum3.multiply(AMW[5] / RSTAR)).multiply(hSign));
- }
- // Equation (24) - J70 Seasonal-Latitudinal Variation
- T capPhi = dateMJD.subtract(36204.0).divide(365.2422);
- capPhi = capPhi.subtract(FastMath.floor(capPhi.getReal()));
- final int signum = (satLat.getReal() >= 0.) ? 1 : -1;
- final T sinLat = satLat.sin();
- final T hm90 = altKm.subtract(90.);
- final T dlrsl = hm90.multiply(0.02).multiply(hm90.multiply(-0.045).exp()).
- multiply(capPhi.multiply(MathUtils.TWO_PI).add(1.72).sin()).
- multiply(signum).multiply(sinLat).multiply(sinLat);
- // Equation (23) - Computes the semiannual variation
- T dlrsa = field.getZero();
- if (z.getReal() < 2000.0) {
- // Use new semiannual model dLog(rho)
- dlrsa = semian08(dayOfYear(dateMJD), altKm, f10B, s10B, xm10B);
- }
- // Sum the delta-log-rhos and apply to the number densities.
- // In CIRA72 the following equation contains an actual sum,
- // namely DLR = LOG10 * (DLRGM + DLRSA + DLRSL)
- // However, for Jacchia 70, there is no DLRGM or DLRSA.
- final T dlr = dlrsl.add(dlrsa).multiply(LOG10);
- for (int i = 0; i < 6; ++i) {
- aln[i] = aln[i].add(dlr);
- }
- // Compute mass-density and mean-molecular-weight and
- // convert number density logs from natural to common.
- T sumnm = field.getZero();
- for (int i = 0; i < 6; ++i) {
- sumnm = sumnm.add(aln[i].exp().multiply(AMW[i]));
- }
- rho = sumnm.divide(AVOGAD);
- // Compute the high altitude exospheric density correction factor
- T fex = field.getOne();
- if (altKm.getReal() >= 1000.0 && altKm.getReal() < 1500.0) {
- final T zeta = altKm.subtract(1000.).multiply(0.002);
- final double f15c = CHT[0] + CHT[1] * f10B + (CHT[2] + CHT[3] * f10B) * 1500.0;
- final double f15cZeta = (CHT[2] + CHT[3] * f10B) * 500.0;
- final double fex2 = 3.0 * f15c - f15cZeta - 3.0;
- final double fex3 = f15cZeta - 2.0 * f15c + 2.0;
- fex = fex.add(zeta.multiply(zeta).multiply(zeta.multiply(fex3).add(fex2)));
- } else if (altKm.getReal() >= 1500.0) {
- fex = altKm.multiply(CHT[3] * f10B).add(altKm.multiply(CHT[2])).add(CHT[0] + CHT[1] * f10B);
- }
- // Apply the exospheric density correction factor.
- rho = rho.multiply(fex);
- return rho;
- }
- /** Compute daily temperature correction for Jacchia-Bowman model.
- * @param f10 solar flux index
- * @param solarTime local solar time (hours in [0, 24[)
- * @param satLat sat lat (radians)
- * @param satAlt height (km)
- * @return dTc correction
- */
- private static double dTc(final double f10, final double solarTime,
- final double satLat, final double satAlt) {
- double dTc = 0.;
- final double st = solarTime / 24.0;
- final double cs = FastMath.cos(satLat);
- final double fs = (f10 - 100.0) / 100.0;
- // Calculates dTc according to height
- if (satAlt >= 120 && satAlt <= 200) {
- final double dtc200 = poly2CDTC(fs, st, cs);
- final double dtc200dz = poly1CDTC(fs, st, cs);
- final double cc = 3.0 * dtc200 - dtc200dz;
- final double dd = dtc200 - cc;
- final double zp = (satAlt - 120.0) / 80.0;
- dTc = zp * zp * (cc + dd * zp);
- } else if (satAlt > 200.0 && satAlt <= 240.0) {
- final double h = (satAlt - 200.0) / 50.0;
- dTc = poly1CDTC(fs, st, cs) * h + poly2CDTC(fs, st, cs);
- } else if (satAlt > 240.0 && satAlt <= 300.0) {
- final double h = 0.8;
- final double bb = poly1CDTC(fs, st, cs);
- final double aa = bb * h + poly2CDTC(fs, st, cs);
- final double p2BDT = poly2BDTC(st);
- final double dtc300 = poly1BDTC(fs, st, cs, 3 * p2BDT);
- final double dtc300dz = cs * p2BDT;
- final double cc = 3.0 * dtc300 - dtc300dz - 3.0 * aa - 2.0 * bb;
- final double dd = dtc300 - aa - bb - cc;
- final double zp = (satAlt - 240.0) / 60.0;
- dTc = aa + zp * (bb + zp * (cc + zp * dd));
- } else if (satAlt > 300.0 && satAlt <= 600.0) {
- final double h = satAlt / 100.0;
- dTc = poly1BDTC(fs, st, cs, h * poly2BDTC(st));
- } else if (satAlt > 600.0 && satAlt <= 800.0) {
- final double poly2 = poly2BDTC(st);
- final double aa = poly1BDTC(fs, st, cs, 6 * poly2);
- final double bb = cs * poly2;
- final double cc = -(3.0 * aa + 4.0 * bb) / 4.0;
- final double dd = (aa + bb) / 4.0;
- final double zp = (satAlt - 600.0) / 100.0;
- dTc = aa + zp * (bb + zp * (cc + zp * dd));
- }
- return dTc;
- }
- /** Compute daily temperature correction for Jacchia-Bowman model.
- * @param f10 solar flux index
- * @param solarTime local solar time (hours in [0, 24[)
- * @param satLat sat lat (radians)
- * @param satAlt height (km)
- * @param <T> type of the filed elements
- * @return dTc correction
- */
- private static <T extends CalculusFieldElement<T>> T dTc(final double f10, final T solarTime,
- final T satLat, final T satAlt) {
- T dTc = solarTime.getField().getZero();
- final T st = solarTime.divide(24.0);
- final T cs = satLat.cos();
- final double fs = (f10 - 100.0) / 100.0;
- // Calculates dTc according to height
- if (satAlt.getReal() >= 120 && satAlt.getReal() <= 200) {
- final T dtc200 = poly2CDTC(fs, st, cs);
- final T dtc200dz = poly1CDTC(fs, st, cs);
- final T cc = dtc200.multiply(3).subtract(dtc200dz);
- final T dd = dtc200.subtract(cc);
- final T zp = satAlt.subtract(120.0).divide(80.0);
- dTc = zp.multiply(zp).multiply(cc.add(dd.multiply(zp)));
- } else if (satAlt.getReal() > 200.0 && satAlt.getReal() <= 240.0) {
- final T h = satAlt.subtract(200.0).divide(50.0);
- dTc = poly1CDTC(fs, st, cs).multiply(h).add(poly2CDTC(fs, st, cs));
- } else if (satAlt.getReal() > 240.0 && satAlt.getReal() <= 300.0) {
- final T h = solarTime.getField().getZero().newInstance(0.8);
- final T bb = poly1CDTC(fs, st, cs);
- final T aa = bb.multiply(h).add(poly2CDTC(fs, st, cs));
- final T p2BDT = poly2BDTC(st);
- final T dtc300 = poly1BDTC(fs, st, cs, p2BDT.multiply(3));
- final T dtc300dz = cs.multiply(p2BDT);
- final T cc = dtc300.multiply(3).subtract(dtc300dz).subtract(aa.multiply(3)).subtract(bb.multiply(2));
- final T dd = dtc300.subtract(aa).subtract(bb).subtract(cc);
- final T zp = satAlt.subtract(240.0).divide(60.0);
- dTc = aa.add(zp.multiply(bb.add(zp.multiply(cc.add(zp.multiply(dd))))));
- } else if (satAlt.getReal() > 300.0 && satAlt.getReal() <= 600.0) {
- final T h = satAlt.divide(100.0);
- dTc = poly1BDTC(fs, st, cs, h.multiply(poly2BDTC(st)));
- } else if (satAlt.getReal() > 600.0 && satAlt.getReal() <= 800.0) {
- final T poly2 = poly2BDTC(st);
- final T aa = poly1BDTC(fs, st, cs, poly2.multiply(6));
- final T bb = cs.multiply(poly2);
- final T cc = aa.multiply(3).add(bb.multiply(4)).divide(-4.0);
- final T dd = aa.add(bb).divide(4.0);
- final T zp = satAlt.subtract(600.0).divide(100.0);
- dTc = aa.add(zp.multiply(bb.add(zp.multiply(cc.add(zp.multiply(dd))))));
- }
- return dTc;
- }
- /** Calculates first polynomial with CDTC array.
- * @param fs scaled flux f10
- * @param st local solar time in [0, 1[
- * @param cs cosine of satLat
- * @return the value of the polynomial
- */
- private static double poly1CDTC(final double fs, final double st, final double cs) {
- return CDTC[0] +
- fs * (CDTC[1] + st * (CDTC[2] + st * (CDTC[3] + st * (CDTC[4] + st * (CDTC[5] + st * CDTC[6]))))) +
- cs * st * (CDTC[7] + st * (CDTC[8] + st * (CDTC[9] + st * (CDTC[10] + st * CDTC[11])))) +
- cs * (CDTC[12] + fs * (CDTC[13] + st * (CDTC[14] + st * CDTC[15])));
- }
- /** Calculates first polynomial with CDTC array.
- * @param fs scaled flux f10
- * @param st local solar time in [0, 1[
- * @param cs cosine of satLat
- * @param <T> type of the field elements
- * @return the value of the polynomial
- */
- private static <T extends CalculusFieldElement<T>> T poly1CDTC(final double fs, final T st, final T cs) {
- return st.multiply(CDTC[6]).
- add(CDTC[5]).multiply(st).
- add(CDTC[4]).multiply(st).
- add(CDTC[3]).multiply(st).
- add(CDTC[2]).multiply(st).
- add(CDTC[1]).multiply(fs).
- add(st.multiply(CDTC[11]).
- add(CDTC[10]).multiply(st).
- add(CDTC[ 9]).multiply(st).
- add(CDTC[ 8]).multiply(st).
- add(CDTC[7]).multiply(st).multiply(cs)).
- add(st.multiply(CDTC[15]).
- add(CDTC[14]).multiply(st).
- add(CDTC[13]).multiply(fs).
- add(CDTC[12]).multiply(cs)).
- add(CDTC[0]);
- }
- /** Calculates second polynomial with CDTC array.
- * @param fs scaled flux f10
- * @param st local solar time in [0, 1[
- * @param cs cosine of satLat
- * @return the value of the polynomial
- */
- private static double poly2CDTC(final double fs, final double st, final double cs) {
- return CDTC[16] + st * cs * (CDTC[17] + st * (CDTC[18] + st * CDTC[19])) +
- fs * cs * (CDTC[20] + st * (CDTC[21] + st * CDTC[22]));
- }
- /** Calculates second polynomial with CDTC array.
- * @param fs scaled flux f10
- * @param st local solar time in [0, 1[
- * @param cs cosine of satLat
- * @param <T> type of the field elements
- * @return the value of the polynomial
- */
- private static <T extends CalculusFieldElement<T>> T poly2CDTC(final double fs, final T st, final T cs) {
- return st.multiply(CDTC[19]).
- add(CDTC[18]).multiply(st).
- add(CDTC[17]).multiply(cs).multiply(st).
- add( st.multiply(CDTC[22]).
- add(CDTC[21]).multiply(st).
- add(CDTC[20]).multiply(cs).multiply(fs)).
- add(CDTC[16]);
- }
- /** Calculates first polynomial with BDTC array.
- * @param fs scaled flux f10
- * @param st local solar time in [0, 1[
- * @param cs cosine of satLat
- * @param hp scaled height * poly2BDTC
- * @return the value of the polynomial
- */
- private static double poly1BDTC(final double fs, final double st, final double cs, final double hp) {
- return BDTC[0] +
- fs * (BDTC[1] + st * (BDTC[2] + st * (BDTC[3] + st * (BDTC[4] + st * (BDTC[5] + st * BDTC[6]))))) +
- cs * (st * (BDTC[7] + st * (BDTC[8] + st * (BDTC[9] + st * (BDTC[10] + st * BDTC[11])))) + hp + BDTC[18]);
- }
- /** Calculates first polynomial with BDTC array.
- * @param fs scaled flux f10
- * @param st local solar time in [0, 1[
- * @param cs cosine of satLat
- * @param hp scaled height * poly2BDTC
- * @param <T> type of the field elements
- * @return the value of the polynomial
- */
- private static <T extends CalculusFieldElement<T>> T poly1BDTC(final double fs, final T st, final T cs, final T hp) {
- return st.multiply(BDTC[6]).
- add(BDTC[5]).multiply(st).
- add(BDTC[4]).multiply(st).
- add(BDTC[3]).multiply(st).
- add(BDTC[2]).multiply(st).
- add(BDTC[1]).multiply(fs).
- add( st.multiply(BDTC[11]).
- add(BDTC[10]).multiply(st).
- add(BDTC[ 9]).multiply(st).
- add(BDTC[ 8]).multiply(st).
- add(BDTC[ 7]).multiply(st).
- add(hp).add(BDTC[18]).multiply(cs)).
- add(BDTC[0]);
- }
- /** Calculates second polynomial with BDTC array.
- * @param st local solar time in [0, 1[
- * @return the value of the polynomial
- */
- private static double poly2BDTC(final double st) {
- return BDTC[12] + st * (BDTC[13] + st * (BDTC[14] + st * (BDTC[15] + st * (BDTC[16] + st * BDTC[17]))));
- }
- /** Calculates second polynomial with BDTC array.
- * @param st local solar time in [0, 1[
- * @param <T> type of the field elements
- * @return the value of the polynomial
- */
- private static <T extends CalculusFieldElement<T>> T poly2BDTC(final T st) {
- return st.multiply(BDTC[17]).
- add(BDTC[16]).multiply(st).
- add(BDTC[15]).multiply(st).
- add(BDTC[14]).multiply(st).
- add(BDTC[13]).multiply(st).
- add(BDTC[12]);
- }
- /** Evaluates mean molecualr mass - Equation (1).
- * @param z altitude (km)
- * @return mean molecular mass
- */
- private static double mBar(final double z) {
- final double dz = z - 100.;
- double amb = CMB[6];
- for (int i = 5; i >= 0; --i) {
- amb = dz * amb + CMB[i];
- }
- return amb;
- }
- /** Evaluates mean molecualr mass - Equation (1).
- * @param z altitude (km)
- * @return mean molecular mass
- * @param <T> type of the field elements
- */
- private static <T extends CalculusFieldElement<T>> T mBar(final T z) {
- final T dz = z.subtract(100.);
- T amb = z.getField().getZero().newInstance(CMB[6]);
- for (int i = 5; i >= 0; --i) {
- amb = dz.multiply(amb).add(CMB[i]);
- }
- return amb;
- }
- /** Evaluates the local temperature, Eq. (10) or (13) depending on altitude.
- * @param z altitude
- * @param tc tc array
- * @return temperature profile
- */
- private static double localTemp(final double z, final double[] tc) {
- final double dz = z - 125.;
- if (dz <= 0.) {
- return ((-9.8204695e-6 * dz - 7.3039742e-4) * dz * dz + 1.0) * dz * tc[1] + tc[0];
- } else {
- return tc[0] + tc[2] * FastMath.atan(tc[3] * dz * (1 + 4.5e-6 * FastMath.pow(dz, 2.5)));
- }
- }
- /** Evaluates the local temperature, Eq. (10) or (13) depending on altitude.
- * @param z altitude
- * @param tc tc array
- * @return temperature profile
- * @param <T> type of the field elements
- */
- private static <T extends CalculusFieldElement<T>> T localTemp(final T z, final T[] tc) {
- final T dz = z.subtract(125.);
- if (dz.getReal() <= 0.) {
- return dz.multiply(-9.8204695e-6).subtract(7.3039742e-4).multiply(dz).multiply(dz).add(1.0).multiply(dz).multiply(tc[1]).add(tc[0]);
- } else {
- return dz.multiply(dz).multiply(dz.sqrt()).multiply(4.5e-6).add(1).multiply(dz).multiply(tc[3]).atan().multiply(tc[2]).add(tc[0]);
- }
- }
- /** Evaluates the gravity at the altitude - Equation (8).
- * @param z altitude (km)
- * @return the gravity (m/s2)
- */
- private static double gravity(final double z) {
- final double tmp = 1.0 + z / EARTH_RADIUS;
- return Constants.G0_STANDARD_GRAVITY / (tmp * tmp);
- }
- /** Evaluates the gravity at the altitude - Equation (8).
- * @param z altitude (km)
- * @return the gravity (m/s2)
- * @param <T> type of the field elements
- */
- private static <T extends CalculusFieldElement<T>> T gravity(final T z) {
- final T tmp = z.divide(EARTH_RADIUS).add(1);
- return tmp.multiply(tmp).reciprocal().multiply(Constants.G0_STANDARD_GRAVITY);
- }
- /** Compute semi-annual variation (delta log(rho)).
- * @param doy day of year
- * @param alt height (km)
- * @param f10B average 81-day centered f10
- * @param s10B average 81-day centered s10
- * @param xm10B average 81-day centered xn10
- * @return semi-annual variation
- */
- private static double semian08(final double doy, final double alt,
- final double f10B, final double s10B, final double xm10B) {
- final double htz = alt / 1000.0;
- // COMPUTE NEW 81-DAY CENTERED SOLAR INDEX FOR FZ
- double fsmb = f10B - 0.70 * s10B - 0.04 * xm10B;
- // SEMIANNUAL AMPLITUDE
- final double fzz = FZM[0] + fsmb * (FZM[1] + htz * (FZM[2] + FZM[3] * htz + FZM[4] * fsmb));
- // COMPUTE DAILY 81-DAY CENTERED SOLAR INDEX FOR GT
- fsmb = f10B - 0.75 * s10B - 0.37 * xm10B;
- // SEMIANNUAL PHASE FUNCTION
- final double tau = MathUtils.TWO_PI * (doy - 1.0) / 365;
- final SinCos sc1P = FastMath.sinCos(tau);
- final SinCos sc2P = SinCos.sum(sc1P, sc1P);
- final double gtz = GTM[0] + GTM[1] * sc1P.sin() + GTM[2] * sc1P.cos() + GTM[3] * sc2P.sin() + GTM[4] * sc2P.cos() +
- fsmb * (GTM[5] + GTM[6] * sc1P.sin() + GTM[7] * sc1P.cos() + GTM[8] * sc2P.sin() + GTM[9] * sc2P.cos());
- return FastMath.max(1.0e-6, fzz) * gtz;
- }
- /** Compute semi-annual variation (delta log(rho)).
- * @param doy day of year
- * @param alt height (km)
- * @param f10B average 81-day centered f10
- * @param s10B average 81-day centered s10
- * @param xm10B average 81-day centered xn10
- * @return semi-annual variation
- * @param <T> type of the field elements
- */
- private static <T extends CalculusFieldElement<T>> T semian08(final T doy, final T alt,
- final double f10B, final double s10B, final double xm10B) {
- final T htz = alt.divide(1000.0);
- // COMPUTE NEW 81-DAY CENTERED SOLAR INDEX FOR FZ
- double fsmb = f10B - 0.70 * s10B - 0.04 * xm10B;
- // SEMIANNUAL AMPLITUDE
- final T fzz = htz.multiply(FZM[3]).add(FZM[2] + FZM[4] * fsmb).multiply(htz).add(FZM[1]).multiply(fsmb).add(FZM[0]);
- // COMPUTE DAILY 81-DAY CENTERED SOLAR INDEX FOR GT
- fsmb = f10B - 0.75 * s10B - 0.37 * xm10B;
- // SEMIANNUAL PHASE FUNCTION
- final T tau = doy.subtract(1).divide(365).multiply(MathUtils.TWO_PI);
- final FieldSinCos<T> sc1P = FastMath.sinCos(tau);
- final FieldSinCos<T> sc2P = FieldSinCos.sum(sc1P, sc1P);
- final T gtz = sc2P.cos().multiply(GTM[9]).
- add(sc2P.sin().multiply(GTM[8])).
- add(sc1P.cos().multiply(GTM[7])).
- add(sc1P.sin().multiply(GTM[6])).
- add(GTM[5]).multiply(fsmb).
- add( sc2P.cos().multiply(GTM[4]).
- add(sc2P.sin().multiply(GTM[3])).
- add(sc1P.cos().multiply(GTM[2])).
- add(sc1P.sin().multiply(GTM[1])).
- add(GTM[0]));
- return fzz.getReal() > 1.0e-6 ? gtz.multiply(fzz) : gtz.multiply(1.0e-6);
- }
- /** Compute day of year.
- * @param dateMJD Modified Julian date
- * @return the number days in year
- */
- private static double dayOfYear(final double dateMJD) {
- final double d1950 = dateMJD - 33281;
- int iyday = (int) d1950;
- final double frac = d1950 - iyday;
- iyday = iyday + 364;
- int itemp = iyday / 1461;
- iyday = iyday - itemp * 1461;
- itemp = iyday / 365;
- if (itemp >= 3) {
- itemp = 3;
- }
- iyday = iyday - 365 * itemp + 1;
- return iyday + frac;
- }
- /** Compute day of year.
- * @param dateMJD Modified Julian date
- * @param <T> type of the field elements
- * @return the number days in year
- */
- private static <T extends CalculusFieldElement<T>> T dayOfYear(final T dateMJD) {
- final T d1950 = dateMJD.subtract(33281);
- int iyday = (int) d1950.getReal();
- final T frac = d1950.subtract(iyday);
- iyday = iyday + 364;
- int itemp = iyday / 1461;
- iyday = iyday - itemp * 1461;
- itemp = iyday / 365;
- if (itemp >= 3) {
- itemp = 3;
- }
- iyday = iyday - 365 * itemp + 1;
- return frac.add(iyday);
- }
- // OUTPUT:
- /** Compute min of two values, one double and one field element.
- * @param d double value
- * @param f field element
- * @param <T> type of the field elements
- * @return min value
- */
- private <T extends CalculusFieldElement<T>> T min(final double d, final T f) {
- return (f.getReal() > d) ? f.getField().getZero().newInstance(d) : f;
- }
- /** Compute max of two values, one double and one field element.
- * @param d double value
- * @param f field element
- * @param <T> type of the field elements
- * @return max value
- */
- private <T extends CalculusFieldElement<T>> T max(final double d, final T f) {
- return (f.getReal() <= d) ? f.getField().getZero().newInstance(d) : f;
- }
- /** Get the local density.
- * @param date current date
- * @param position current position in frame
- * @param frame the frame in which is defined the position
- * @return local density (kg/m³)
- */
- public double getDensity(final AbsoluteDate date, final Vector3D position,
- final Frame frame) {
- // check if data are available :
- if (date.compareTo(inputParams.getMaxDate()) > 0 ||
- date.compareTo(inputParams.getMinDate()) < 0) {
- throw new OrekitException(OrekitMessages.NO_SOLAR_ACTIVITY_AT_DATE,
- date, inputParams.getMinDate(), inputParams.getMaxDate());
- }
- // compute MJD date
- final DateTimeComponents dt = date.getComponents(utc);
- final double dateMJD = dt.getDate().getMJD() +
- dt.getTime().getSecondsInLocalDay() / Constants.JULIAN_DAY;
- // compute geodetic position
- final GeodeticPoint inBody = earth.transform(position, frame, date);
- // compute sun position
- final Frame ecef = earth.getBodyFrame();
- final Vector3D sunPos = getSunPosition(date, ecef);
- final GeodeticPoint sunInBody = earth.transform(sunPos, ecef, date);
- return getDensity(dateMJD,
- sunInBody.getLongitude(), sunInBody.getLatitude(),
- inBody.getLongitude(), inBody.getLatitude(), inBody.getAltitude(),
- inputParams.getF10(date), inputParams.getF10B(date),
- inputParams.getS10(date), inputParams.getS10B(date),
- inputParams.getXM10(date), inputParams.getXM10B(date),
- inputParams.getY10(date), inputParams.getY10B(date),
- inputParams.getDSTDTC(date));
- }
- /** {@inheritDoc} */
- @Override
- public <T extends CalculusFieldElement<T>> T getDensity(final FieldAbsoluteDate<T> date,
- final FieldVector3D<T> position,
- final Frame frame) {
- // check if data are available :
- final AbsoluteDate dateD = date.toAbsoluteDate();
- if (dateD.compareTo(inputParams.getMaxDate()) > 0 ||
- dateD.compareTo(inputParams.getMinDate()) < 0) {
- throw new OrekitException(OrekitMessages.NO_SOLAR_ACTIVITY_AT_DATE,
- dateD, inputParams.getMinDate(), inputParams.getMaxDate());
- }
- // compute MJD date
- final DateTimeComponents components = date.getComponents(utc);
- final T dateMJD = date
- .durationFrom(new FieldAbsoluteDate<>(date.getField(), components, utc))
- .add(components.getTime().getSecondsInLocalDay())
- .divide(Constants.JULIAN_DAY)
- .add(components.getDate().getMJD());
- // compute geodetic position (km and °)
- final FieldGeodeticPoint<T> inBody = earth.transform(position, frame, date);
- // compute sun position
- final Frame ecef = earth.getBodyFrame();
- final FieldVector3D<T> sunPos = getSunPosition(date, ecef);
- final FieldGeodeticPoint<T> sunInBody = earth.transform(sunPos, ecef, date);
- return getDensity(dateMJD,
- sunInBody.getLongitude(), sunInBody.getLatitude(),
- inBody.getLongitude(), inBody.getLatitude(), inBody.getAltitude(),
- inputParams.getF10(dateD), inputParams.getF10B(dateD),
- inputParams.getS10(dateD), inputParams.getS10B(dateD),
- inputParams.getXM10(dateD), inputParams.getXM10B(dateD),
- inputParams.getY10(dateD), inputParams.getY10B(dateD),
- inputParams.getDSTDTC(dateD));
- }
- }