FieldDeepSDP4.java

  1. /* Copyright 2002-2022 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.propagation.analytical.tle;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.util.FastMath;
  20. import org.hipparchus.util.FieldSinCos;
  21. import org.hipparchus.util.MathArrays;
  22. import org.hipparchus.util.MathUtils;
  23. import org.hipparchus.util.SinCos;
  24. import org.orekit.annotation.DefaultDataContext;
  25. import org.orekit.attitudes.AttitudeProvider;
  26. import org.orekit.data.DataContext;
  27. import org.orekit.frames.Frame;
  28. import org.orekit.time.DateTimeComponents;
  29. import org.orekit.utils.Constants;


  30. /** This class contains the methods that compute deep space perturbation terms.
  31.  * <p>
  32.  * The user should not bother in this class since it is handled internaly by the
  33.  * {@link TLEPropagator}.
  34.  * </p>
  35.  * <p>This implementation is largely inspired from the paper and source code <a
  36.  * href="https://www.celestrak.com/publications/AIAA/2006-6753/">Revisiting Spacetrack
  37.  * Report #3</a> and is fully compliant with its results and tests cases.</p>
  38.  * @author Felix R. Hoots, Ronald L. Roehrich, December 1980 (original fortran)
  39.  * @author David A. Vallado, Paul Crawford, Richard Hujsak, T.S. Kelso (C++ translation and improvements)
  40.  * @author Fabien Maussion (java translation)
  41.  * @author Thomas Paulet (field translation)
  42.  * @since 11.0
  43.  */
  44. public class FieldDeepSDP4<T extends CalculusFieldElement<T>> extends FieldSDP4<T> {

  45.     // CHECKSTYLE: stop JavadocVariable check

  46.     /** Integration step (seconds). */
  47.     private static final double SECULAR_INTEGRATION_STEP  = 720.0;

  48.     /** Intermediate values. */
  49.     private double thgr;
  50.     private T xnq;
  51.     private T omegaq;
  52.     private double zcosil;
  53.     private double zsinil;
  54.     private double zsinhl;
  55.     private double zcoshl;
  56.     private double zmol;
  57.     private double zcosgl;
  58.     private double zsingl;
  59.     private double zmos;
  60.     private T savtsn;

  61.     private T ee2;
  62.     private T e3;
  63.     private T xi2;
  64.     private T xi3;
  65.     private T xl2;
  66.     private T xl3;
  67.     private T xl4;
  68.     private T xgh2;
  69.     private T xgh3;
  70.     private T xgh4;
  71.     private T xh2;
  72.     private T xh3;

  73.     private T d2201;
  74.     private T d2211;
  75.     private T d3210;
  76.     private T d3222;
  77.     private T d4410;
  78.     private T d4422;
  79.     private T d5220;
  80.     private T d5232;
  81.     private T d5421;
  82.     private T d5433;
  83.     private T xlamo;

  84.     private T sse;
  85.     private T ssi;
  86.     private T ssl;
  87.     private T ssh;
  88.     private T ssg;
  89.     private T se2;
  90.     private T si2;
  91.     private T sl2;
  92.     private T sgh2;
  93.     private T sh2;
  94.     private T se3;
  95.     private T si3;
  96.     private T sl3;
  97.     private T sgh3;
  98.     private T sh3;
  99.     private T sl4;
  100.     private T sgh4;

  101.     private T del1;
  102.     private T del2;
  103.     private T del3;
  104.     private T xfact;
  105.     private T xli;
  106.     private T xni;
  107.     private T atime;

  108.     private T pe;
  109.     private T pinc;
  110.     private T pl;
  111.     private T pgh;
  112.     private T ph;

  113.     private T[] derivs;

  114.     // CHECKSTYLE: resume JavadocVariable check

  115.     /** Flag for resonant orbits. */
  116.     private boolean resonant;

  117.     /** Flag for synchronous orbits. */
  118.     private boolean synchronous;

  119.     /** Flag for compliance with Dundee modifications. */
  120.     private boolean isDundeeCompliant = true;

  121.     /** Constructor for a unique initial TLE.
  122.      *
  123.      * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
  124.      *
  125.      * @param initialTLE the TLE to propagate.
  126.      * @param attitudeProvider provider for attitude computation
  127.      * @param mass spacecraft mass (kg)
  128.      * @param parameters SGP4 and SDP4 model parameters
  129.      * @see #FieldDeepSDP4(FieldTLE, AttitudeProvider, CalculusFieldElement, Frame, CalculusFieldElement[])
  130.      */
  131.     @DefaultDataContext
  132.     public FieldDeepSDP4(final FieldTLE<T> initialTLE, final AttitudeProvider attitudeProvider,
  133.                     final T mass, final T[] parameters) {
  134.         this(initialTLE, attitudeProvider, mass,
  135.                 DataContext.getDefault().getFrames().getTEME(), parameters);
  136.     }

  137.     /** Constructor for a unique initial TLE.
  138.      * @param initialTLE the TLE to propagate.
  139.      * @param attitudeProvider provider for attitude computation
  140.      * @param mass spacecraft mass (kg)
  141.      * @param teme the TEME frame to use for propagation.
  142.      * @param parameters SGP4 and SDP4 model parameters
  143.      */
  144.     public FieldDeepSDP4(final FieldTLE<T> initialTLE,
  145.                          final AttitudeProvider attitudeProvider,
  146.                          final T mass,
  147.                          final Frame teme,
  148.                          final T[] parameters) {
  149.         super(initialTLE, attitudeProvider, mass, teme, parameters);
  150.     }

  151.     /** Computes luni - solar terms from initial coordinates and epoch.
  152.      */
  153.     protected void luniSolarTermsComputation() {

  154.         final T zero = tle.getPerigeeArgument().getField().getZero();
  155.         final T pi   = zero.getPi();

  156.         final FieldSinCos<T> scg  = FastMath.sinCos(tle.getPerigeeArgument());
  157.         final T sing = scg.sin();
  158.         final T cosg = scg.cos();

  159.         final FieldSinCos<T> scq  = FastMath.sinCos(tle.getRaan());
  160.         final T sinq = scq.sin();
  161.         final T cosq = scq.cos();
  162.         final T aqnv = a0dp.reciprocal();

  163.         // Compute julian days since 1900
  164.         final double daysSince1900 = (tle.getDate()
  165.                 .getComponents(utc)
  166.                 .offsetFrom(DateTimeComponents.JULIAN_EPOCH)) /
  167.                 Constants.JULIAN_DAY - 2415020;

  168.         double cc = TLEConstants.C1SS;
  169.         double ze = TLEConstants.ZES;
  170.         double zn = TLEConstants.ZNS;
  171.         T zsinh = sinq;
  172.         T zcosh = cosq;

  173.         thgr = thetaG(tle.getDate());
  174.         xnq = xn0dp;
  175.         omegaq = tle.getPerigeeArgument();

  176.         final double xnodce = 4.5236020 - 9.2422029e-4 * daysSince1900;
  177.         final SinCos scTem  = FastMath.sinCos(xnodce);
  178.         final double stem = scTem.sin();
  179.         final double ctem = scTem.cos();
  180.         final double c_minus_gam = 0.228027132 * daysSince1900 - 1.1151842;
  181.         final double gam = 5.8351514 + 0.0019443680 * daysSince1900;

  182.         zcosil = 0.91375164 - 0.03568096 * ctem;
  183.         zsinil = FastMath.sqrt(1.0 - zcosil * zcosil);
  184.         zsinhl = 0.089683511 * stem / zsinil;
  185.         zcoshl = FastMath.sqrt(1.0 - zsinhl * zsinhl);
  186.         zmol = MathUtils.normalizeAngle(c_minus_gam, pi.getReal());

  187.         double zx = 0.39785416 * stem / zsinil;
  188.         final double zy = zcoshl * ctem + 0.91744867 * zsinhl * stem;
  189.         zx = FastMath.atan2( zx, zy) + gam - xnodce;
  190.         final SinCos scZx = FastMath.sinCos(zx);
  191.         zcosgl = scZx.cos();
  192.         zsingl = scZx.sin();
  193.         zmos = MathUtils.normalizeAngle(6.2565837 + 0.017201977 * daysSince1900, pi.getReal());

  194.         // Do solar terms
  195.         savtsn = zero.add(1e20);

  196.         T zcosi = zero.add(0.91744867);
  197.         T zsini = zero.add(0.39785416);
  198.         T zsing = zero.add(-0.98088458);
  199.         T zcosg = zero.add(0.1945905);

  200.         T se =  zero;
  201.         T sgh = zero;
  202.         T sh =  zero;
  203.         T si =  zero;
  204.         T sl =  zero;

  205.         // There was previously some convoluted logic here, but it boils
  206.         // down to this:  we compute the solar terms,  then the lunar terms.
  207.         // On a second pass,  we recompute the solar terms, taking advantage
  208.         // of the improved data that resulted from computing lunar terms.
  209.         for (int iteration = 0; iteration < 2; ++iteration) {
  210.             final T a1  = zcosh.multiply(zcosg).add(zsinh.multiply(zsing).multiply(zcosi));
  211.             final T a3  = zcosh.multiply(zsing.negate()).add(zsinh.multiply(zcosg).multiply(zcosi));
  212.             final T a7  = zsinh.negate().multiply(zcosg).add(zcosh.multiply(zcosi).multiply(zsing));
  213.             final T a8  = zsing.multiply(zsini);
  214.             final T a9  = zsinh.multiply(zsing).add(zcosh.multiply(zcosi).multiply(zcosg));
  215.             final T a10 = zcosg.multiply(zsini);
  216.             final T a2  = cosi0.multiply(a7).add(sini0.multiply(a8));
  217.             final T a4  = cosi0.multiply(a9).add(sini0.multiply(a10));
  218.             final T a5  = sini0.negate().multiply(a7).add(cosi0.multiply(a8));
  219.             final T a6  = sini0.negate().multiply(a9).add(cosi0.multiply(a10));
  220.             final T x1  = a1.multiply(cosg).add(a2.multiply(sing));
  221.             final T x2  = a3.multiply(cosg).add(a4.multiply(sing));
  222.             final T x3  = a1.negate().multiply(sing).add(a2.multiply(cosg));
  223.             final T x4  = a3.negate().multiply(sing).add(a4.multiply(cosg));
  224.             final T x5  = a5.multiply(sing);
  225.             final T x6  = a6.multiply(sing);
  226.             final T x7  = a5.multiply(cosg);
  227.             final T x8  = a6.multiply(cosg);
  228.             final T z31 = x1.multiply(x1).multiply(12).subtract(x3.multiply(x3).multiply(3));
  229.             final T z32 = x1.multiply(x2).multiply(24).subtract(x3.multiply(x4).multiply(6));
  230.             final T z33 = x2.multiply(x2).multiply(12).subtract(x4.multiply(x4).multiply(3));
  231.             final T z11 = a1.multiply(-6).multiply(a5).add(e0sq.multiply(x1.multiply(x7).multiply(-24).add(x3.multiply(x5).multiply(-6))));
  232.             final T z12 = a1.multiply(a6).add(a3.multiply(a5)).multiply(-6).add(
  233.                                 e0sq.multiply(x2.multiply(x7).add(x1.multiply(x8)).multiply(-24).add(
  234.                                 x3.multiply(x6).add(x4.multiply(x5)).multiply(-6))));
  235.             final T z13 = a3.multiply(a6).multiply(-6).add(e0sq.multiply(
  236.                                x2.multiply(x8).multiply(-24).subtract(x4.multiply(x6).multiply(6))));
  237.             final T z21 = a2.multiply(a5).multiply(6).add(e0sq.multiply(
  238.                                x1.multiply(x5).multiply(24).subtract(x3.multiply(x7).multiply(6))));
  239.             final T z22 = a4.multiply(a5).add(a2.multiply(a6)).multiply(6).add(
  240.                                e0sq.multiply(x2.multiply(x5).add(x1.multiply(x6)).multiply(24).subtract(
  241.                                x4.multiply(x7).add(x3.multiply(x8)).multiply(6))));
  242.             final T z23 = a4.multiply(a6).multiply(6).add(e0sq.multiply(x2.multiply(x6).multiply(24).subtract(x4.multiply(x8).multiply(6))));
  243.             final T s3  = xnq.reciprocal().multiply(cc);
  244.             final T s2  = beta0.reciprocal().multiply(s3.multiply(-0.5));
  245.             final T s4  = s3.multiply(beta0);
  246.             final T s1  = tle.getE().multiply(s4).multiply(-15);
  247.             final T s5  = x1.multiply(x3).add(x2.multiply(x4));
  248.             final T s6  = x2.multiply(x3).add(x1.multiply(x4));
  249.             final T s7  = x2.multiply(x4).subtract(x1.multiply(x3));
  250.             T z1 = a1.multiply(a1).add(a2.multiply(a2)).multiply(3).add(z31.multiply(e0sq));
  251.             T z2 = a1.multiply(a3).add(a2.multiply(a4)).multiply(6).add(z32.multiply(e0sq));
  252.             T z3 = a3.multiply(a3).add(a4.multiply(a4)).multiply(3).add(z33.multiply(e0sq));

  253.             z1 = z1.add(z1).add(beta02.multiply(z31));
  254.             z2 = z2.add(z2).add(beta02.multiply(z32));
  255.             z3 = z3.add(z3).add(beta02.multiply(z33));
  256.             se = s1.multiply(zn).multiply(s5);
  257.             si = s2.multiply(zn).multiply(z11.add(z13));
  258.             sl = s3.multiply(-zn).multiply(z1.add(z3).subtract(14).subtract(e0sq.multiply(6)));
  259.             sgh = s4.multiply(zn).multiply(z31.add(z33).subtract(6));
  260.             if (tle.getI().getReal() < pi.divide(60.0).getReal()) {
  261.                 // inclination smaller than 3 degrees
  262.                 sh = zero;
  263.             } else {
  264.                 sh = s2.multiply(-zn).multiply(z21.add(z23));
  265.             }
  266.             ee2  = s1.multiply(s6).multiply(2);
  267.             e3   = s1.multiply(s7).multiply(2);
  268.             xi2  = s2.multiply(z12).multiply(2);
  269.             xi3  = s2.multiply(z13.subtract(z11)).multiply(2);
  270.             xl2  = s3.multiply(z2).multiply(-2);
  271.             xl3  = s3.multiply(z3.subtract(z1)).multiply(-2);
  272.             xl4  = s3.multiply(e0sq.multiply(-9).add(-21)).multiply(ze).multiply(-2);
  273.             xgh2 = s4.multiply(z32).multiply(2);
  274.             xgh3 = s4.multiply(z33.subtract(z31)).multiply(2);
  275.             xgh4 = s4.multiply(ze).multiply(-18);
  276.             xh2  = s2.multiply(z22).multiply(-2);
  277.             xh3  = s2.multiply(z23.subtract(z21)).multiply(-2);

  278.             if (iteration == 0) { // we compute lunar terms only on the first pass:
  279.                 sse = se;
  280.                 ssi = si;
  281.                 ssl = sl;
  282.                 ssh = (tle.getI().getReal() < pi.divide(60.0).getReal()) ? zero : sh.divide(sini0);
  283.                 ssg = sgh.subtract(cosi0.multiply(ssh));
  284.                 se2 = ee2;
  285.                 si2 = xi2;
  286.                 sl2 = xl2;
  287.                 sgh2 = xgh2;
  288.                 sh2 = xh2;
  289.                 se3 = e3;
  290.                 si3 = xi3;
  291.                 sl3 = xl3;
  292.                 sgh3 = xgh3;
  293.                 sh3 = xh3;
  294.                 sl4 = xl4;
  295.                 sgh4 = xgh4;
  296.                 zcosg = zero.add(zcosgl);
  297.                 zsing = zero.add(zsingl);
  298.                 zcosi = zero.add(zcosil);
  299.                 zsini = zero.add(zsinil);
  300.                 zcosh = cosq.multiply(zcoshl).add(sinq.multiply(zsinhl));
  301.                 zsinh = sinq.multiply(zcoshl).subtract(cosq.multiply(zsinhl));
  302.                 zn = TLEConstants.ZNL;
  303.                 cc = TLEConstants.C1L;
  304.                 ze = TLEConstants.ZEL;
  305.             }
  306.         } // end of solar - lunar - solar terms computation

  307.         sse = sse.add(se);
  308.         ssi = ssi.add(si);
  309.         ssl = ssl.add(sl);
  310.         ssg = ssg.add(sgh).subtract((tle.getI().getReal() < pi.divide(60.0).getReal()) ? zero : (cosi0.divide(sini0).multiply(sh)));
  311.         ssh = ssh.add((tle.getI().getReal() < pi.divide(60.0).getReal()) ? zero : sh.divide(sini0));



  312.         //        Start the resonant-synchronous tests and initialization

  313.         T bfact = zero;

  314.         // if mean motion is 1.893053 to 2.117652 revs/day, and eccentricity >= 0.5,
  315.         // start of the 12-hour orbit, e > 0.5 section
  316.         if (xnq.getReal() >= 0.00826 && xnq.getReal() <= 0.00924 && tle.getE().getReal() >= 0.5) {

  317.             final T g201  = tle.getE().subtract(0.64).negate().multiply(0.440).add(-0.306);
  318.             final T eoc   = tle.getE().multiply(e0sq);
  319.             final T sini2 = sini0.multiply(sini0);
  320.             final T f220  = cosi0.multiply(2).add(theta2).add(1).multiply(0.75);
  321.             final T f221  = sini2.multiply(1.5);
  322.             final T f321  = sini0.multiply(1.875).multiply(cosi0.multiply(2).negate().subtract(theta2.multiply(3)).add(1));
  323.             final T f322  = sini0.multiply(-1.875).multiply(cosi0.multiply(2).subtract(theta2.multiply(3)).add(1));
  324.             final T f441  = sini2.multiply(f220).multiply(35);
  325.             final T f442  = sini2.multiply(sini2).multiply(39.3750);
  326.             final T f522  = sini0.multiply(9.84375).multiply(sini2.multiply(cosi0.multiply(-2).add(theta2.multiply(-5)).add(1.0)).add(
  327.                                     cosi0.multiply(4.0).add(theta2.multiply(6.0)).add(-2).multiply(0.33333333)));
  328.             final T f523  = sini0.multiply(sini2.multiply(cosi0.multiply(-4).add(theta2.multiply(10)).add(-2)).multiply(4.92187512).add(
  329.                                     cosi0.multiply(2).subtract(theta2.multiply(3)).add(1).multiply(6.56250012)));
  330.             final T f542  = sini0.multiply(29.53125).multiply(cosi0.multiply(-8).add(2).add(
  331.                                     theta2.multiply(cosi0.multiply(8).add(theta2.multiply(10)).add(-12))));
  332.             final T f543  = sini0.multiply(29.53125).multiply(cosi0.multiply(-8).add(-2).add(
  333.                                     theta2.multiply(cosi0.multiply(8).subtract(theta2.multiply(10)).add(12))));
  334.             final T g211;
  335.             final T g310;
  336.             final T g322;
  337.             final T g410;
  338.             final T g422;
  339.             final T g520;

  340.             resonant = true;       // it is resonant...
  341.             synchronous = false;     // but it's not synchronous

  342.             // Geopotential resonance initialization for 12 hour orbits :
  343.             if (tle.getE().getReal() <= 0.65) {
  344.                 g211 = tle.getE().multiply( -13.247).add(  e0sq.multiply(   16.290)).add(                                  3.616);
  345.                 g310 = tle.getE().multiply( 117.390).add(  e0sq.multiply( -228.419)).add(  eoc.multiply( 156.591)).add(  -19.302);
  346.                 g322 = tle.getE().multiply(109.7927).add(  e0sq.multiply(-214.6334)).add(  eoc.multiply(146.5816)).add( -18.9068);
  347.                 g410 = tle.getE().multiply( 242.694).add(  e0sq.multiply( -471.094)).add(  eoc.multiply( 313.953)).add(  -41.122);
  348.                 g422 = tle.getE().multiply( 841.880).add(  e0sq.multiply(-1629.014)).add(  eoc.multiply(1083.435)).add( -146.407);
  349.                 g520 = tle.getE().multiply(3017.977).add(  e0sq.multiply(-5740.032)).add(  eoc.multiply(3708.276)).add( -532.114);
  350.             } else  {
  351.                 g211 = tle.getE().multiply( 331.819).add(  e0sq.multiply( -508.738)).add(  eoc.multiply( 266.724)).add(  -72.099);
  352.                 g310 = tle.getE().multiply(1582.851).add(  e0sq.multiply(-2415.925)).add(  eoc.multiply(1246.113)).add( -346.844);
  353.                 g322 = tle.getE().multiply(1554.908).add(  e0sq.multiply(-2366.899)).add(  eoc.multiply(1215.972)).add( -342.585);
  354.                 g410 = tle.getE().multiply(4758.686).add(  e0sq.multiply(-7193.992)).add(  eoc.multiply(3651.957)).add(-1052.797);
  355.                 g422 = tle.getE().multiply(16178.11).add(  e0sq.multiply(-24462.77)).add(  eoc.multiply(12422.52)).add( -3581.69);
  356.                 if (tle.getE().getReal() <= 0.715) {
  357.                     g520 = tle.getE().multiply(-4664.75).add(  e0sq.multiply(  3763.64)).add(                                1464.74);
  358.                 } else {
  359.                     g520 = tle.getE().multiply(29936.92).add(  e0sq.multiply(-54087.36)).add(  eoc.multiply(31324.56)).add( -5149.66);
  360.                 }
  361.             }

  362.             final T g533;
  363.             final T g521;
  364.             final T g532;
  365.             if (tle.getE().getReal() < 0.7) {
  366.                 g533 = tle.getE().multiply(  4988.61).add(  e0sq.multiply(  -9064.77)).add(  eoc.multiply(  5542.21)).add(  -919.2277);
  367.                 g521 = tle.getE().multiply(4568.6173).add(  e0sq.multiply(-8491.4146)).add(  eoc.multiply( 5337.524)).add( -822.71072);
  368.                 g532 = tle.getE().multiply(  4690.25).add(  e0sq.multiply(  -8624.77)).add(  eoc.multiply(   5341.4)).add(   -853.666);
  369.             } else {
  370.                 g533 = tle.getE().multiply(161616.52).add(  e0sq.multiply( -229838.2)).add(  eoc.multiply(109377.94)).add(  -37995.78);
  371.                 g521 = tle.getE().multiply(218913.95).add(  e0sq.multiply(-309468.16)).add(  eoc.multiply(146349.42)).add( -51752.104);
  372.                 g532 = tle.getE().multiply(170470.89).add(  e0sq.multiply(-242699.48)).add(  eoc.multiply(115605.82)).add(  -40023.88);
  373.             }

  374.             T temp1 = xnq.multiply(xnq).multiply(aqnv).multiply(aqnv).multiply(3);
  375.             T temp  = temp1.multiply(TLEConstants.ROOT22);
  376.             d2201   = temp.multiply(f220).multiply(g201);
  377.             d2211   = temp.multiply(f221).multiply(g211);
  378.             temp1   = temp1.multiply(aqnv);
  379.             temp    = temp1.multiply(TLEConstants.ROOT32);
  380.             d3210   = temp.multiply(f321).multiply(g310);
  381.             d3222   = temp.multiply(f322).multiply(g322);
  382.             temp1   = temp1.multiply(aqnv);
  383.             temp    = temp1.multiply(2 * TLEConstants.ROOT44);
  384.             d4410   = temp.multiply(f441).multiply(g410);
  385.             d4422   = temp.multiply(f442).multiply(g422);
  386.             temp1   = temp1.multiply(aqnv);
  387.             temp    = temp1.multiply(TLEConstants.ROOT52);
  388.             d5220   = temp.multiply(f522).multiply(g520);
  389.             d5232   = temp.multiply(f523).multiply(g532);
  390.             temp    = temp1.multiply(2 * TLEConstants.ROOT54);
  391.             d5421   = temp.multiply(f542).multiply(g521);
  392.             d5433   = temp.multiply(f543).multiply(g533);
  393.             xlamo   = tle.getMeanAnomaly().add(tle.getRaan()).add(tle.getRaan()).subtract(thgr + thgr);
  394.             bfact   = xmdot.add(xnodot).add(xnodot).subtract(TLEConstants.THDT + TLEConstants.THDT);
  395.             bfact   = bfact.add(ssl).add(ssh).add(ssh);
  396.         } else if (xnq.getReal() < 0.0052359877 && xnq.getReal() > 0.0034906585) {
  397.             // if mean motion is .8 to 1.2 revs/day : (geosynch)

  398.             final T cosio_plus_1 = cosi0.add(1.0);
  399.             final T g200 = e0sq.multiply(e0sq.multiply(0.8125).add(-2.5)).add(1);
  400.             final T g300 = e0sq.multiply(e0sq.multiply(6.60937).add(-6)).add(1);
  401.             final T f311 = sini0.multiply(0.9375).multiply(sini0.multiply(cosi0.multiply(3).add(1))).subtract(cosio_plus_1.multiply(0.75));
  402.             final T g310 = e0sq.multiply(2).add(1);
  403.             final T f220 = cosio_plus_1.multiply(cosio_plus_1).multiply(0.75);
  404.             final T f330 = f220.multiply(cosio_plus_1).multiply(2.5);

  405.             resonant = true;
  406.             synchronous = true;

  407.             // Synchronous resonance terms initialization
  408.             del1 = xnq.multiply(xnq).multiply(aqnv).multiply(aqnv).multiply(3);
  409.             del2 = del1.multiply(f220).multiply(g200).multiply(2 * TLEConstants.Q22);
  410.             del3 = del1.multiply(f330).multiply(g300).multiply(aqnv).multiply(3 * TLEConstants.Q33);
  411.             del1 = del1.multiply(f311).multiply(g310).multiply(TLEConstants.Q31).multiply(aqnv);
  412.             xlamo = tle.getMeanAnomaly().add(tle.getRaan()).add(tle.getPerigeeArgument()).subtract(thgr);
  413.             bfact = xmdot.add(omgdot).add(xnodot).subtract(TLEConstants.THDT);
  414.             bfact = bfact.add(ssl).add(ssg).add(ssh);
  415.         } else {
  416.             // it's neither a high-e 12-hours orbit nor a geosynchronous:
  417.             resonant = false;
  418.             synchronous = false;
  419.         }

  420.         if (resonant) {
  421.             xfact = bfact.subtract(xnq);

  422.             // Initialize integrator
  423.             xli   = xlamo;
  424.             xni   = xnq;
  425.             atime = zero;
  426.         }
  427.         derivs = MathArrays.buildArray(xnq.getField(), 2);
  428.     }

  429.     /** Computes secular terms from current coordinates and epoch.
  430.      * @param t offset from initial epoch (minutes)
  431.      */
  432.     protected void deepSecularEffects(final T t)  {

  433.         xll     = xll.add(ssl.multiply(t));
  434.         omgadf  = omgadf.add(ssg.multiply(t));
  435.         xnode   = xnode.add(ssh.multiply(t));
  436.         em      = tle.getE().add(sse.multiply(t));
  437.         xinc    = tle.getI().add(ssi.multiply(t));

  438.         if (resonant) {
  439.             // If we're closer to t = 0 than to the currently-stored data
  440.             // from the previous call to this function,  then we're
  441.             // better off "restarting",  going back to the initial data.
  442.             // The Dundee code rigs things up to _always_ take 720-minute
  443.             // steps from epoch to end time,  except for the final step.
  444.             // Easiest way to arrange similar behavior in this code is
  445.             // just to always do a restart,  if we're in Dundee-compliant
  446.             // mode.
  447.             if (FastMath.abs(t).getReal() < FastMath.abs(t.subtract(atime)).getReal() || isDundeeCompliant)  {
  448.                 // Epoch restart
  449.                 atime = t.getField().getZero();
  450.                 xni = xnq;
  451.                 xli = xlamo;
  452.             }
  453.             boolean lastIntegrationStep = false;
  454.             // if |step|>|step max| then do one step at step max
  455.             while (!lastIntegrationStep) {
  456.                 double delt = t.subtract(atime).getReal();
  457.                 if (delt > SECULAR_INTEGRATION_STEP) {
  458.                     delt = SECULAR_INTEGRATION_STEP;
  459.                 } else if (delt < -SECULAR_INTEGRATION_STEP) {
  460.                     delt = -SECULAR_INTEGRATION_STEP;
  461.                 } else {
  462.                     lastIntegrationStep = true;
  463.                 }

  464.                 computeSecularDerivs();

  465.                 final T xldot = xni.add(xfact);

  466.                 T xlpow = t.getField().getZero().add(1.);
  467.                 xli = xli.add(xldot.multiply(delt));
  468.                 xni = xni.add(derivs[0].multiply(delt));
  469.                 double delt_factor = delt;
  470.                 xlpow = xlpow.multiply(xldot);
  471.                 derivs[1] = derivs[1].multiply(xlpow);
  472.                 delt_factor *= delt / 2;
  473.                 xli = xli.add(derivs[0].multiply(delt_factor));
  474.                 xni = xni.add(derivs[1].multiply(delt_factor));
  475.                 atime = atime.add(delt);
  476.             }
  477.             xn = xni;
  478.             final T temp = xnode.negate().add(thgr).add(t.multiply(TLEConstants.THDT));
  479.             xll = xli.add(temp).add(synchronous ? omgadf.negate() : temp);
  480.         }
  481.     }

  482.     /** Computes periodic terms from current coordinates and epoch.
  483.      * @param t offset from initial epoch (min)
  484.      */
  485.     protected void deepPeriodicEffects(final T t)  {

  486.         // If the time didn't change by more than 30 minutes,
  487.         // there's no good reason to recompute the perturbations;
  488.         // they don't change enough over so short a time span.
  489.         // However,  the Dundee code _always_ recomputes,  so if
  490.         // we're attempting to replicate its results,  we've gotta
  491.         // recompute everything,  too.
  492.         if (FastMath.abs(savtsn.subtract(t).getReal()) >= 30.0 || isDundeeCompliant)  {

  493.             savtsn = t;

  494.             // Update solar perturbations for time T
  495.             T zm = t.multiply(TLEConstants.ZNS).add(zmos);
  496.             T zf = zm.add(FastMath.sin(zm).multiply(2 * TLEConstants.ZES));
  497.             FieldSinCos<T> sczf = FastMath.sinCos(zf);
  498.             T sinzf = sczf.sin();
  499.             T f2 = sinzf.multiply(sinzf).multiply(0.5).subtract(0.25);
  500.             T f3 = sinzf.multiply(sczf.cos()).multiply(-0.5);
  501.             final T ses = se2.multiply(f2).add(se3.multiply(f3));
  502.             final T sis = si2.multiply(f2).add(si3.multiply(f3));
  503.             final T sls = sl2.multiply(f2).add(sl3.multiply(f3)).add(sl4.multiply(sinzf));
  504.             final T sghs = sgh2.multiply(f2).add(sgh3.multiply(f3)).add(sgh4.multiply(sinzf));
  505.             final T shs = sh2.multiply(f2).add(sh3.multiply(f3));

  506.             // Update lunar perturbations for time T
  507.             zm = t.multiply(TLEConstants.ZNL).add(zmol);
  508.             zf = zm.add(FastMath.sin(zm).multiply(2 * TLEConstants.ZEL));
  509.             sczf = FastMath.sinCos(zf);
  510.             sinzf = sczf.sin();
  511.             f2 =  sinzf.multiply(sinzf).multiply(0.5).subtract(0.25);
  512.             f3 = sinzf.multiply(sczf.cos()).multiply(-0.5);
  513.             final T sel = ee2.multiply(f2).add(e3.multiply(f3));
  514.             final T sil = xi2.multiply(f2).add(xi3.multiply(f3));
  515.             final T sll = xl2.multiply(f2).add(xl3.multiply(f3)).add(xl4.multiply(sinzf));
  516.             final T sghl = xgh2.multiply(f2).add(xgh3.multiply(f3)).add(xgh4.multiply(sinzf));
  517.             final T sh1 = xh2.multiply(f2).add(xh3.multiply(f3));

  518.             // Sum the solar and lunar contributions
  519.             pe   = ses.add(sel);
  520.             pinc = sis.add(sil);
  521.             pl   = sls.add(sll);
  522.             pgh  = sghs.add(sghl);
  523.             ph   = shs.add(sh1);
  524.         }

  525.         xinc = xinc.add(pinc);

  526.         final FieldSinCos<T> scis = FastMath.sinCos(xinc);
  527.         final T sinis = scis.sin();
  528.         final T cosis = scis.cos();

  529.         /* Add solar/lunar perturbation correction to eccentricity: */
  530.         em     = em.add(pe);
  531.         xll    = xll.add(pl);
  532.         omgadf = omgadf.add(pgh);
  533.         xinc   = MathUtils.normalizeAngle(xinc, t.getField().getZero());

  534.         if (FastMath.abs(xinc).getReal() >= 0.2) {
  535.             // Apply periodics directly
  536.             final T temp_val = ph.divide(sinis);
  537.             omgadf = omgadf.subtract(cosis.multiply(temp_val));
  538.             xnode  = xnode.add(temp_val);
  539.         } else {
  540.             // Apply periodics with Lyddane modification
  541.             final FieldSinCos<T> scok = FastMath.sinCos(xnode);
  542.             final T sinok = scok.sin();
  543.             final T cosok = scok.cos();
  544.             final T alfdp =  ph.multiply(cosok).add((pinc.multiply(cosis).add(sinis)).multiply(sinok));
  545.             final T betdp = ph.negate().multiply(sinok).add((pinc.multiply(cosis).add(sinis)).multiply(cosok));
  546.             final T delta_xnode = MathUtils.normalizeAngle(FastMath.atan2(alfdp, betdp).subtract(xnode), t.getField().getZero());
  547.             final T dls = xnode.negate().multiply(sinis).multiply(pinc);
  548.             omgadf = omgadf.add(dls.subtract(cosis.multiply(delta_xnode)));
  549.             xnode  = xnode.add(delta_xnode);
  550.         }
  551.     }

  552.     /** Computes internal secular derivs. */
  553.     private void computeSecularDerivs() {

  554.         final FieldSinCos<T> sc_li  = FastMath.sinCos(xli);
  555.         final T sin_li = sc_li.sin();
  556.         final T cos_li = sc_li.cos();
  557.         final T sin_2li = sin_li.multiply(cos_li).multiply(2.);
  558.         final T cos_2li = cos_li.multiply(cos_li).multiply(2.).subtract(1.);

  559.         // Dot terms calculated :
  560.         if (synchronous)  {
  561.             final T sin_3li = sin_2li.multiply(cos_li).add(cos_2li.multiply(sin_li));
  562.             final T cos_3li = cos_2li.multiply(cos_li).subtract(sin_2li.multiply(sin_li));
  563.             final T term1a = del1.multiply(sin_li .multiply(TLEConstants.C_FASX2) .subtract(cos_li .multiply(TLEConstants.S_FASX2 )));
  564.             final T term2a = del2.multiply(sin_2li.multiply(TLEConstants.C_2FASX4).subtract(cos_2li.multiply(TLEConstants.S_2FASX4)));
  565.             final T term3a = del3.multiply(sin_3li.multiply(TLEConstants.C_3FASX6).subtract(cos_3li.multiply(TLEConstants.S_3FASX6)));
  566.             final T term1b = del1.multiply(cos_li .multiply(TLEConstants.C_FASX2)      .add(sin_li .multiply(TLEConstants.S_FASX2 )));
  567.             final T term2b = del2.multiply(cos_2li.multiply(TLEConstants.C_2FASX4)     .add(sin_2li.multiply(TLEConstants.S_2FASX4))).multiply(2.0);
  568.             final T term3b = del3.multiply(cos_3li.multiply(TLEConstants.C_3FASX6)     .add(sin_3li.multiply(TLEConstants.S_3FASX6))).multiply(3.0);
  569.             derivs[0] = term1a.add(term2a).add(term3a);
  570.             derivs[1] = term1b.add(term2b).add(term3b);
  571.         } else {
  572.             // orbit is a 12-hour resonant one
  573.             final T xomi = omegaq.add(omgdot.multiply(atime));
  574.             final FieldSinCos<T> sc_omi  = FastMath.sinCos(xomi);
  575.             final T sin_omi = sc_omi.sin();
  576.             final T cos_omi = sc_omi.cos();
  577.             final T sin_li_m_omi = sin_li.multiply(cos_omi).subtract(sin_omi.multiply(cos_li));
  578.             final T sin_li_p_omi = sin_li.multiply(cos_omi).add(     sin_omi.multiply(cos_li));
  579.             final T cos_li_m_omi = cos_li.multiply(cos_omi).add(     sin_omi.multiply(sin_li));
  580.             final T cos_li_p_omi = cos_li.multiply(cos_omi).subtract(sin_omi.multiply(sin_li));
  581.             final T sin_2omi = sin_omi.multiply(cos_omi).multiply(2.0);
  582.             final T cos_2omi = cos_omi.multiply(cos_omi).multiply(2.0).subtract(1.0);
  583.             final T sin_2li_m_omi  = sin_2li.multiply(cos_omi ).subtract(sin_omi .multiply(cos_2li));
  584.             final T sin_2li_p_omi  = sin_2li.multiply(cos_omi ).add(     sin_omi .multiply(cos_2li));
  585.             final T cos_2li_m_omi  = cos_2li.multiply(cos_omi ).add(     sin_omi .multiply(sin_2li));
  586.             final T cos_2li_p_omi  = cos_2li.multiply(cos_omi ).subtract(sin_omi .multiply(sin_2li));
  587.             final T sin_2li_p_2omi = sin_2li.multiply(cos_2omi).add(     sin_2omi.multiply(cos_2li));
  588.             final T cos_2li_p_2omi = cos_2li.multiply(cos_2omi).subtract(sin_2omi.multiply(sin_2li));
  589.             final T sin_2omi_p_li  = sin_li .multiply(cos_2omi).add(     sin_2omi.multiply(cos_li ));
  590.             final T cos_2omi_p_li  = cos_li .multiply(cos_2omi).subtract(sin_2omi.multiply(sin_li ));
  591.             final T term1a = d2201.multiply(sin_2omi_p_li .multiply(TLEConstants.C_G22).subtract(cos_2omi_p_li .multiply(TLEConstants.S_G22))) .add(
  592.                              d2211.multiply(sin_li        .multiply(TLEConstants.C_G22).subtract(cos_li        .multiply(TLEConstants.S_G22)))).add(
  593.                              d3210.multiply(sin_li_p_omi  .multiply(TLEConstants.C_G32).subtract(cos_li_p_omi  .multiply(TLEConstants.S_G32)))).add(
  594.                              d3222.multiply(sin_li_m_omi  .multiply(TLEConstants.C_G32).subtract(cos_li_m_omi  .multiply(TLEConstants.S_G32)))).add(
  595.                              d5220.multiply(sin_li_p_omi  .multiply(TLEConstants.C_G52).subtract(cos_li_p_omi  .multiply(TLEConstants.S_G52)))).add(
  596.                              d5232.multiply(sin_li_m_omi  .multiply(TLEConstants.C_G52).subtract(cos_li_m_omi  .multiply(TLEConstants.S_G52))));
  597.             final T term2a = d4410.multiply(sin_2li_p_2omi.multiply(TLEConstants.C_G44).subtract(cos_2li_p_2omi.multiply(TLEConstants.S_G44))) .add(
  598.                              d4422.multiply(sin_2li       .multiply(TLEConstants.C_G44).subtract(cos_2li       .multiply(TLEConstants.S_G44)))).add(
  599.                              d5421.multiply(sin_2li_p_omi .multiply(TLEConstants.C_G54).subtract(cos_2li_p_omi .multiply(TLEConstants.S_G54)))).add(
  600.                              d5433.multiply(sin_2li_m_omi .multiply(TLEConstants.C_G54).subtract(cos_2li_m_omi .multiply(TLEConstants.S_G54))));
  601.             final T term1b = d2201.multiply(cos_2omi_p_li .multiply(TLEConstants.C_G22)     .add(sin_2omi_p_li .multiply(TLEConstants.S_G22))) .add(
  602.                              d2211.multiply(cos_li        .multiply(TLEConstants.C_G22)     .add(sin_li        .multiply(TLEConstants.S_G22)))).add(
  603.                              d3210.multiply(cos_li_p_omi  .multiply(TLEConstants.C_G32)     .add(sin_li_p_omi  .multiply(TLEConstants.S_G32)))).add(
  604.                              d3222.multiply(cos_li_m_omi  .multiply(TLEConstants.C_G32)     .add(sin_li_m_omi  .multiply(TLEConstants.S_G32)))).add(
  605.                              d5220.multiply(cos_li_p_omi  .multiply(TLEConstants.C_G52)     .add(sin_li_p_omi  .multiply(TLEConstants.S_G52)))).add(
  606.                              d5232.multiply(cos_li_m_omi  .multiply(TLEConstants.C_G52)     .add(sin_li_m_omi  .multiply(TLEConstants.S_G52))));
  607.             final T term2b = d4410.multiply(cos_2li_p_2omi.multiply(TLEConstants.C_G44)     .add(sin_2li_p_2omi.multiply(TLEConstants.S_G44))) .add(
  608.                              d4422.multiply(cos_2li       .multiply(TLEConstants.C_G44)     .add(sin_2li       .multiply(TLEConstants.S_G44)))).add(
  609.                              d5421.multiply(cos_2li_p_omi .multiply(TLEConstants.C_G54)     .add(sin_2li_p_omi .multiply(TLEConstants.S_G54)))).add(
  610.                              d5433.multiply(cos_2li_m_omi .multiply(TLEConstants.C_G54)     .add(sin_2li_m_omi .multiply(TLEConstants.S_G54)))).multiply(2.0);

  611.             derivs[0] = term1a.add(term2a);
  612.             derivs[1] = term1b.add(term2b);

  613.         }
  614.     }

  615. }