1   /* Copyright 2002-2015 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.propagation.semianalytical.dsst.forces;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.SortedMap;
22  import java.util.TreeMap;
23  
24  import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
25  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
26  import org.apache.commons.math3.util.FastMath;
27  import org.apache.commons.math3.util.MathUtils;
28  import org.orekit.attitudes.AttitudeProvider;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
31  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
32  import org.orekit.frames.Frame;
33  import org.orekit.frames.Transform;
34  import org.orekit.orbits.Orbit;
35  import org.orekit.orbits.OrbitType;
36  import org.orekit.orbits.PositionAngle;
37  import org.orekit.propagation.SpacecraftState;
38  import org.orekit.propagation.events.EventDetector;
39  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
40  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
41  import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
42  import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
43  import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
44  import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
45  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenTesseralLinear;
46  import org.orekit.time.AbsoluteDate;
47  
48  /** Tesseral contribution to the {@link DSSTCentralBody central body gravitational
49   *  perturbation}.
50   *  <p>
51   *  Only resonant tesserals are considered.
52   *  </p>
53   *
54   *  @author Romain Di Costanzo
55   *  @author Pascal Parraud
56   */
57  class TesseralContribution implements DSSTForceModel {
58  
59      /** Minimum period for analytically averaged high-order resonant
60       *  central body spherical harmonics in seconds.
61       */
62      private static final double MIN_PERIOD_IN_SECONDS = 864000.;
63  
64      /** Minimum period for analytically averaged high-order resonant
65       *  central body spherical harmonics in satellite revolutions.
66       */
67      private static final double MIN_PERIOD_IN_SAT_REV = 10.;
68  
69      /** Number of points for interpolation. */
70      private static final int INTERPOLATION_POINTS = 3;
71  
72      /** Maximum possible (absolute) value for j index. */
73      private static final int MAXJ = 12;
74  
75      /** The maximum degree used for tesseral short periodics (without m-daily). */
76      private static final int MAX_DEGREE_TESSERAL_SP = 8;
77  
78      /** The maximum degree used for m-daily tesseral short periodics. */
79      private static final int MAX_DEGREE_MDAILY_TESSERAL_SP = 12;
80  
81      /** The maximum order used for tesseral short periodics (without m-daily). */
82      private static final int MAX_ORDER_TESSERAL_SP = 8;
83  
84      /** The maximum order used for m-daily tesseral short periodics. */
85      private static final int MAX_ORDER_MDAILY_TESSERAL_SP = 12;
86  
87      /** The maximum value for eccentricity power. */
88      private static final int MAX_ECCPOWER_SP = 4;
89  
90      /** Provider for spherical harmonics. */
91      private final UnnormalizedSphericalHarmonicsProvider provider;
92  
93      /** Central body rotating frame. */
94      private final Frame bodyFrame;
95  
96      /** Central body rotation rate (rad/s). */
97      private final double centralBodyRotationRate;
98  
99      /** Central body rotation period (seconds). */
100     private final double bodyPeriod;
101 
102     /** Maximal degree to consider for harmonics potential. */
103     private final int maxDegree;
104 
105     /** Maximal degree to consider for short periodics tesseral harmonics potential (without m-daily). */
106     private final int maxDegreeTesseralSP;
107 
108     /** Maximal degree to consider for short periodics m-daily tesseral harmonics potential . */
109     private final int maxDegreeMdailyTesseralSP;
110 
111     /** Maximal order to consider for harmonics potential. */
112     private final int maxOrder;
113 
114     /** Maximal order to consider for short periodics tesseral harmonics potential (without m-daily). */
115     private final int maxOrderTesseralSP;
116 
117     /** Maximal order to consider for short periodics m-daily tesseral harmonics potential . */
118     private final int maxOrderMdailyTesseralSP;
119 
120     /** List of resonant orders. */
121     private final List<Integer> resOrders;
122 
123     /** Factorial. */
124     private final double[] fact;
125 
126     /** Maximum power of the eccentricity to use in summation over s. */
127     private int maxEccPow;
128 
129     /** Maximum power of the eccentricity to use in summation over s for
130      * short periodic tesseral harmonics (without m-daily). */
131     private int maxEccPowTesseralSP;
132 
133     /** Maximum power of the eccentricity to use in summation over s for
134      * m-daily tesseral harmonics. */
135     private int maxEccPowMdailyTesseralSP;
136 
137     /** Maximum power of the eccentricity to use in Hansen coefficient Kernel expansion. */
138     private int maxHansen;
139 
140     /** Keplerian period. */
141     private double orbitPeriod;
142 
143     /** Ratio of satellite period to central body rotation period. */
144     private double ratio;
145 
146     /** Retrograde factor. */
147     private int    I;
148 
149     // Equinoctial elements (according to DSST notation)
150     /** a. */
151     private double a;
152 
153     /** ex. */
154     private double k;
155 
156     /** ey. */
157     private double h;
158 
159     /** hx. */
160     private double q;
161 
162     /** hy. */
163     private double p;
164 
165     /** lm. */
166     private double lm;
167 
168     /** Eccentricity. */
169     private double ecc;
170 
171     // Common factors for potential computation
172     /** &Chi; = 1 / sqrt(1 - e²) = 1 / B. */
173     private double chi;
174 
175     /** &Chi;². */
176     private double chi2;
177 
178     // Equinoctial reference frame vectors (according to DSST notation)
179     /** Equinoctial frame f vector. */
180     private Vector3D f;
181 
182     /** Equinoctial frame g vector. */
183     private Vector3D g;
184 
185     /** Central body rotation angle θ. */
186     private double theta;
187 
188     /** Direction cosine α. */
189     private double alpha;
190 
191     /** Direction cosine β. */
192     private double beta;
193 
194     /** Direction cosine γ. */
195     private double gamma;
196 
197     // Common factors from equinoctial coefficients
198     /** 2 * a / A .*/
199     private double ax2oA;
200 
201     /** 1 / (A * B) .*/
202     private double ooAB;
203 
204     /** B / A .*/
205     private double BoA;
206 
207     /** B / (A * (1 + B)) .*/
208     private double BoABpo;
209 
210     /** C / (2 * A * B) .*/
211     private double Co2AB;
212 
213     /** μ / a .*/
214     private double moa;
215 
216     /** R / a .*/
217     private double roa;
218 
219     /** ecc². */
220     private double e2;
221 
222     /** The satellite mean motion. */
223     private double meanMotion;
224 
225     /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
226     private final boolean mDailiesOnly;
227 
228     /** Maximum value for j.
229      * <p>
230      * jmax = maxDegreeTesseralSP + maxEccPowTesseralSP, no more than 12
231      * </p>
232      * */
233     private int jMax;
234 
235     /** List of non resonant orders with j != 0. */
236     private final SortedMap<Integer, List<Integer> > nonResOrders;
237 
238     /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
239      * The indexes are s + maxDegree and j */
240     private HansenTesseralLinear[][] hansenObjects;
241 
242     /** The C<sub>i</sub><sup>j</sup><sup>m</sup> and S<sub>i</sub><sup>j</sup><sup>m</sup> coefficients
243      * used to compute the short-periodic tesseral contribution. */
244     private TesseralShortPeriodicCoefficients tesseralSPCoefs;
245 
246     /** The frame used to describe the orbits. */
247     private Frame frame;
248 
249     /** Single constructor.
250      *  @param centralBodyFrame rotating body frame
251      *  @param centralBodyRotationRate central body rotation rate (rad/s)
252      *  @param provider provider for spherical harmonics
253      *  @param mDailiesOnly if true only M-dailies tesseral harmonics are taken into account for short periodics
254      */
255     public TesseralContribution(final Frame centralBodyFrame,
256                                 final double centralBodyRotationRate,
257                                 final UnnormalizedSphericalHarmonicsProvider provider,
258                                 final boolean mDailiesOnly) {
259 
260         // Central body rotating frame
261         this.bodyFrame = centralBodyFrame;
262 
263         //Save the rotation rate
264         this.centralBodyRotationRate = centralBodyRotationRate;
265 
266         // Central body rotation period in seconds
267         this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;
268 
269         // Provider for spherical harmonics
270         this.provider  = provider;
271         this.maxDegree = provider.getMaxDegree();
272         this.maxOrder  = provider.getMaxOrder();
273 
274         //set the maximum degree order for short periodics
275         this.maxDegreeTesseralSP = FastMath.min(maxDegree, MAX_DEGREE_TESSERAL_SP);
276         this.maxDegreeMdailyTesseralSP = FastMath.min(maxDegree, MAX_DEGREE_MDAILY_TESSERAL_SP);
277         this.maxOrderTesseralSP = FastMath.min(maxOrder, MAX_ORDER_TESSERAL_SP);
278         this.maxOrderMdailyTesseralSP = FastMath.min(maxOrder, MAX_ORDER_MDAILY_TESSERAL_SP);
279 
280         // set the maximum value for eccentricity power
281         this.maxEccPowTesseralSP = MAX_ECCPOWER_SP;
282         this.maxEccPowMdailyTesseralSP = FastMath.min(maxDegreeMdailyTesseralSP - 2, MAX_ECCPOWER_SP);
283         this.jMax = FastMath.min(MAXJ, maxDegreeTesseralSP + maxEccPowTesseralSP);
284 
285         // m-daylies only
286         this.mDailiesOnly = mDailiesOnly;
287 
288         // Initialize default values
289         this.resOrders = new ArrayList<Integer>();
290         this.nonResOrders = new TreeMap<Integer, List <Integer> >();
291         this.maxEccPow = 0;
292         this.maxHansen = 0;
293 
294        // Factorials computation
295         final int maxFact = 2 * maxDegree + 1;
296         this.fact = new double[maxFact];
297         fact[0] = 1;
298         for (int i = 1; i < maxFact; i++) {
299             fact[i] = i * fact[i - 1];
300         }
301     }
302 
303     /** {@inheritDoc} */
304     @Override
305     public void initialize(final AuxiliaryElements aux, final boolean meanOnly)
306         throws OrekitException {
307 
308         // Keplerian period
309         orbitPeriod = aux.getKeplerianPeriod();
310 
311         // orbit frame
312         frame = aux.getFrame();
313 
314         // Set the highest power of the eccentricity in the analytical power
315         // series expansion for the averaged high order resonant central body
316         // spherical harmonic perturbation
317         final double e = aux.getEcc();
318         if (e <= 0.005) {
319             maxEccPow = 3;
320         } else if (e <= 0.02) {
321             maxEccPow = 4;
322         } else if (e <= 0.1) {
323             maxEccPow = 7;
324         } else if (e <= 0.2) {
325             maxEccPow = 10;
326         } else if (e <= 0.3) {
327             maxEccPow = 12;
328         } else if (e <= 0.4) {
329             maxEccPow = 15;
330         } else {
331             maxEccPow = 20;
332         }
333 
334         // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
335         maxHansen = maxEccPow / 2;
336         jMax = FastMath.min(MAXJ, maxDegree + maxEccPow);
337 
338         // Ratio of satellite to central body periods to define resonant terms
339         ratio = orbitPeriod / bodyPeriod;
340 
341         // Compute the resonant tesseral harmonic terms if not set by the user
342         getResonantAndNonResonantTerms(meanOnly);
343 
344         //initialize the HansenTesseralLinear objects needed
345         createHansenObjects(meanOnly);
346 
347         if (!meanOnly) {
348             //Initialize the Tesseral Short Periodics coefficient class
349             tesseralSPCoefs = new TesseralShortPeriodicCoefficients(jMax,
350                     FastMath.max(maxOrderTesseralSP, maxOrderMdailyTesseralSP), INTERPOLATION_POINTS);
351         }
352     }
353 
354     /** Create the objects needed for linear transformation.
355      *
356      * <p>
357      * Each {@link org.orekit.propagation.semianalytical.dsst.utilities.hansenHansenTesseralLinear HansenTesseralLinear} uses
358      * a fixed value for s and j. Since j varies from -maxJ to +maxJ and s varies from -maxDegree to +maxDegree,
359      * a 2 * maxDegree + 1 x 2 * maxJ + 1 matrix of objects should be created. The size of this matrix can be reduced
360      * by taking into account the expression (2.7.3-2). This means that is enough to create the objects for  positive
361      * values of j and all values of s.
362      * </p>
363      *
364      * @param meanOnly create only the objects required for the mean contribution
365      */
366     private void createHansenObjects(final boolean meanOnly) {
367         //Allocate the two dimensional array
368         this.hansenObjects = new HansenTesseralLinear[2 * maxDegree + 1][jMax + 1];
369 
370         if (meanOnly) {
371             // loop through the resonant orders
372             for (int m : resOrders) {
373                 //Compute the corresponding j term
374                 final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
375 
376                 //Compute the sMin and sMax values
377                 final int sMin = FastMath.min(maxEccPow - j, maxDegree);
378                 final int sMax = FastMath.min(maxEccPow + j, maxDegree);
379 
380                 //loop through the s values
381                 for (int s = 0; s <= sMax; s++) {
382                     //Compute the n0 value
383                     final int n0 = FastMath.max(FastMath.max(2, m), s);
384 
385                     //Create the object for the pair j,s
386                     this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
387 
388                     if (s > 0 && s <= sMin) {
389                         //Also create the object for the pair j, -s
390                         this.hansenObjects[maxDegree - s][j] =  new HansenTesseralLinear(maxDegree, -s, j, n0, maxHansen);
391                     }
392                 }
393             }
394         } else {
395             // create all objects
396             for (int j = 0; j <= jMax; j++) {
397                 for (int s = -maxDegree; s <= maxDegree; s++) {
398                     //Compute the n0 value
399                     final int n0 = FastMath.max(2, FastMath.abs(s));
400 
401                     this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
402                 }
403             }
404         }
405     }
406 
407     /** {@inheritDoc} */
408     @Override
409     public void initializeStep(final AuxiliaryElements aux) throws OrekitException {
410 
411         // Equinoctial elements
412         a  = aux.getSma();
413         k  = aux.getK();
414         h  = aux.getH();
415         q  = aux.getQ();
416         p  = aux.getP();
417         lm = aux.getLM();
418 
419         // Eccentricity
420         ecc = aux.getEcc();
421         e2 = ecc * ecc;
422 
423         // Retrograde factor
424         I = aux.getRetrogradeFactor();
425 
426         // Equinoctial frame vectors
427         f = aux.getVectorF();
428         g = aux.getVectorG();
429 
430         // Central body rotation angle from equation 2.7.1-(3)(4).
431         final Transform t = bodyFrame.getTransformTo(aux.getFrame(), aux.getDate());
432         final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
433         final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
434         theta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
435                                 f.dotProduct(xB) + I * g.dotProduct(yB));
436 
437         // Direction cosines
438         alpha = aux.getAlpha();
439         beta  = aux.getBeta();
440         gamma = aux.getGamma();
441 
442         // Equinoctial coefficients
443         // A = sqrt(μ * a)
444         final double A = aux.getA();
445         // B = sqrt(1 - h² - k²)
446         final double B = aux.getB();
447         // C = 1 + p² + q²
448         final double C = aux.getC();
449         // Common factors from equinoctial coefficients
450         // 2 * a / A
451         ax2oA  = 2. * a / A;
452         // B / A
453         BoA  = B / A;
454         // 1 / AB
455         ooAB = 1. / (A * B);
456         // C / 2AB
457         Co2AB = C * ooAB / 2.;
458         // B / (A * (1 + B))
459         BoABpo = BoA / (1. + B);
460         // &mu / a
461         moa = provider.getMu() / a;
462         // R / a
463         roa = provider.getAe() / a;
464 
465         // &Chi; = 1 / B
466         chi = 1. / B;
467         chi2 = chi * chi;
468 
469         //mean motion n
470         meanMotion = aux.getMeanMotion();
471     }
472 
473     /** {@inheritDoc} */
474     @Override
475     public double[] getMeanElementRate(final SpacecraftState spacecraftState) throws OrekitException {
476 
477         // Compute potential derivatives
478         final double[] dU  = computeUDerivatives(spacecraftState.getDate());
479         final double dUda  = dU[0];
480         final double dUdh  = dU[1];
481         final double dUdk  = dU[2];
482         final double dUdl  = dU[3];
483         final double dUdAl = dU[4];
484         final double dUdBe = dU[5];
485         final double dUdGa = dU[6];
486 
487         // Compute the cross derivative operator :
488         final double UAlphaGamma   = alpha * dUdGa - gamma * dUdAl;
489         final double UAlphaBeta    = alpha * dUdBe - beta  * dUdAl;
490         final double UBetaGamma    =  beta * dUdGa - gamma * dUdBe;
491         final double Uhk           =     h * dUdk  -     k * dUdh;
492         final double pUagmIqUbgoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;
493         final double UhkmUabmdUdl  =  Uhk - UAlphaBeta - dUdl;
494 
495         final double da =  ax2oA * dUdl;
496         final double dh =    BoA * dUdk + k * pUagmIqUbgoAB - h * BoABpo * dUdl;
497         final double dk =  -(BoA * dUdh + h * pUagmIqUbgoAB + k * BoABpo * dUdl);
498         final double dp =  Co2AB * (p * UhkmUabmdUdl - UBetaGamma);
499         final double dq =  Co2AB * (q * UhkmUabmdUdl - I * UAlphaGamma);
500         final double dM = -ax2oA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUagmIqUbgoAB;
501 
502         return new double[] {da, dk, dh, dq, dp, dM};
503     }
504 
505     /** {@inheritDoc} */
506     @Override
507     public double[] getShortPeriodicVariations(final AbsoluteDate date,
508                                                final double[] meanElements)
509         throws OrekitException {
510 
511         // Initialise the short periodic variations
512         final double[] shortPeriodicVariation = new double[] {0., 0., 0., 0., 0., 0.};
513 
514         // Compute only if there is at least one non-resonant tesseral or
515         // only the m-daily tesseral should be taken into account
516         if (!nonResOrders.isEmpty() || mDailiesOnly) {
517 
518             // Build an Orbit object from the mean elements
519             final Orbit meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(
520                     meanElements, PositionAngle.MEAN, date, provider.getMu(), this.frame);
521 
522             //Build an auxiliary object
523             final AuxiliaryElements aux = new AuxiliaryElements(meanOrbit, I);
524 
525             // Central body rotation angle from equation 2.7.1-(3)(4).
526             final Transform t = bodyFrame.getTransformTo(aux.getFrame(), aux.getDate());
527             final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
528             final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
529             final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
530                                                         f.dotProduct(xB) + I * g.dotProduct(yB));
531 
532             //Add the m-daily contribution
533             for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
534                 // Phase angle
535                 final double jlMmt  = -m * currentTheta;
536                 final double sinPhi = FastMath.sin(jlMmt);
537                 final double cosPhi = FastMath.cos(jlMmt);
538 
539                 // compute contribution for each element
540                 for (int i = 0; i < 6; i++) {
541                     shortPeriodicVariation[i] += tesseralSPCoefs.getCijm(i, 0, m, date) * cosPhi +
542                                                  tesseralSPCoefs.getSijm(i, 0, m, date) * sinPhi;
543                 }
544             }
545 
546             // loop through all non-resonant (j,m) pairs
547             for (int m : nonResOrders.keySet()) {
548                 final List<Integer> listJ = nonResOrders.get(m);
549 
550                 for (int j : listJ) {
551                     // Phase angle
552                     final double jlMmt  = j * meanElements[5] - m * currentTheta;
553                     final double sinPhi = FastMath.sin(jlMmt);
554                     final double cosPhi = FastMath.cos(jlMmt);
555 
556                     // compute contribution for each element
557                     for (int i = 0; i < 6; i++) {
558                         shortPeriodicVariation[i] += tesseralSPCoefs.getCijm(i, j, m, date) * cosPhi +
559                                                      tesseralSPCoefs.getSijm(i, j, m, date) * sinPhi;
560                     }
561 
562                 }
563             }
564         }
565 
566         return shortPeriodicVariation;
567     }
568 
569     /** {@inheritDoc} */
570     @Override
571     public EventDetector[] getEventsDetectors() {
572         return null;
573     }
574 
575     /** {@inheritDoc} */
576     public void computeShortPeriodicsCoefficients(final SpacecraftState state) throws OrekitException {
577         // Initialise the Hansen coefficients
578         for (int s = -maxDegree; s <= maxDegree; s++) {
579             // coefficients with j == 0 are always needed
580             this.hansenObjects[s + maxDegree][0].computeInitValues(e2, chi, chi2);
581             if (!mDailiesOnly) {
582                 // initialize other objects only if required
583                 for (int j = 1; j <= jMax; j++) {
584                     this.hansenObjects[s + maxDegree][j].computeInitValues(e2, chi, chi2);
585                 }
586             }
587         }
588 
589         // Compute coefficients
590         tesseralSPCoefs.computeCoefficients(state.getDate());
591     }
592 
593      /**
594       * Get the resonant and non-resonant tesseral terms in the central body spherical harmonic field.
595       *
596       * @param resonantOnly extract only resonant terms
597       */
598     private void getResonantAndNonResonantTerms(final boolean resonantOnly) {
599 
600         // Compute natural resonant terms
601         final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
602                                                    MIN_PERIOD_IN_SECONDS / orbitPeriod);
603 
604         // Search the resonant orders in the tesseral harmonic field
605         resOrders.clear();
606         nonResOrders.clear();
607         for (int m = 1; m <= maxOrder; m++) {
608             final double resonance = ratio * m;
609             int jRes = 0;
610             final int jComputedRes = (int) FastMath.round(resonance);
611             if (jComputedRes > 0 && jComputedRes <= jMax && FastMath.abs(resonance - jComputedRes) <= tolerance) {
612                 // Store each resonant index and order
613                 resOrders.add(m);
614                 jRes = jComputedRes;
615             }
616 
617             if (!resonantOnly && !mDailiesOnly && m <= maxOrderTesseralSP) {
618                 //compute non resonant orders in the tesseral harmonic field
619                 final List<Integer> listJofM = new ArrayList<Integer>();
620                 //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree)
621                 for (int j = -jMax; j <= jMax; j++) {
622                     if (j != 0 && j != jRes) {
623                         listJofM.add(j);
624                     }
625                 }
626 
627                 nonResOrders.put(m, listJofM);
628             }
629         }
630     }
631 
632     /** Computes the potential U derivatives.
633      *  <p>The following elements are computed from expression 3.3 - (4).
634      *  <pre>
635      *  dU / da
636      *  dU / dh
637      *  dU / dk
638      *  dU / dλ
639      *  dU / dα
640      *  dU / dβ
641      *  dU / dγ
642      *  </pre>
643      *  </p>
644      *
645      *  @param date current date
646      *  @return potential derivatives
647      *  @throws OrekitException if an error occurs
648      */
649     private double[] computeUDerivatives(final AbsoluteDate date) throws OrekitException {
650 
651         // Potential derivatives
652         double dUda  = 0.;
653         double dUdh  = 0.;
654         double dUdk  = 0.;
655         double dUdl  = 0.;
656         double dUdAl = 0.;
657         double dUdBe = 0.;
658         double dUdGa = 0.;
659 
660         // Compute only if there is at least one resonant tesseral
661         if (!resOrders.isEmpty()) {
662             // Gmsj and Hmsj polynomials
663             final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(k, h, alpha, beta, I);
664 
665             // GAMMAmns function
666             final GammaMnsFunction gammaMNS = new GammaMnsFunction(fact, gamma, I);
667 
668             // R / a up to power degree
669             final double[] roaPow = new double[maxDegree + 1];
670             roaPow[0] = 1.;
671             for (int i = 1; i <= maxDegree; i++) {
672                 roaPow[i] = roa * roaPow[i - 1];
673             }
674 
675             // SUM over resonant terms {j,m}
676             for (int m : resOrders) {
677 
678                 // Resonant index for the current resonant order
679                 final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
680 
681                 // Phase angle
682                 final double jlMmt  = j * lm - m * theta;
683                 final double sinPhi = FastMath.sin(jlMmt);
684                 final double cosPhi = FastMath.cos(jlMmt);
685 
686                 // Potential derivatives components for a given resonant pair {j,m}
687                 double dUdaCos  = 0.;
688                 double dUdaSin  = 0.;
689                 double dUdhCos  = 0.;
690                 double dUdhSin  = 0.;
691                 double dUdkCos  = 0.;
692                 double dUdkSin  = 0.;
693                 double dUdlCos  = 0.;
694                 double dUdlSin  = 0.;
695                 double dUdAlCos = 0.;
696                 double dUdAlSin = 0.;
697                 double dUdBeCos = 0.;
698                 double dUdBeSin = 0.;
699                 double dUdGaCos = 0.;
700                 double dUdGaSin = 0.;
701 
702                 // s-SUM from -sMin to sMax
703                 final int sMin = FastMath.min(maxEccPow - j, maxDegree);
704                 final int sMax = FastMath.min(maxEccPow + j, maxDegree);
705                 for (int s = 0; s <= sMax; s++) {
706 
707                     //Compute the initial values for Hansen coefficients using newComb operators
708                     this.hansenObjects[s + maxDegree][j].computeInitValues(e2, chi, chi2);
709 
710                     // n-SUM for s positive
711                     final double[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
712                                                             roaPow, ghMSJ, gammaMNS);
713                     dUdaCos  += nSumSpos[0][0];
714                     dUdaSin  += nSumSpos[0][1];
715                     dUdhCos  += nSumSpos[1][0];
716                     dUdhSin  += nSumSpos[1][1];
717                     dUdkCos  += nSumSpos[2][0];
718                     dUdkSin  += nSumSpos[2][1];
719                     dUdlCos  += nSumSpos[3][0];
720                     dUdlSin  += nSumSpos[3][1];
721                     dUdAlCos += nSumSpos[4][0];
722                     dUdAlSin += nSumSpos[4][1];
723                     dUdBeCos += nSumSpos[5][0];
724                     dUdBeSin += nSumSpos[5][1];
725                     dUdGaCos += nSumSpos[6][0];
726                     dUdGaSin += nSumSpos[6][1];
727 
728                     // n-SUM for s negative
729                     if (s > 0 && s <= sMin) {
730                         //Compute the initial values for Hansen coefficients using newComb operators
731                         this.hansenObjects[maxDegree - s][j].computeInitValues(e2, chi, chi2);
732 
733                         final double[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
734                                                                 roaPow, ghMSJ, gammaMNS);
735                         dUdaCos  += nSumSneg[0][0];
736                         dUdaSin  += nSumSneg[0][1];
737                         dUdhCos  += nSumSneg[1][0];
738                         dUdhSin  += nSumSneg[1][1];
739                         dUdkCos  += nSumSneg[2][0];
740                         dUdkSin  += nSumSneg[2][1];
741                         dUdlCos  += nSumSneg[3][0];
742                         dUdlSin  += nSumSneg[3][1];
743                         dUdAlCos += nSumSneg[4][0];
744                         dUdAlSin += nSumSneg[4][1];
745                         dUdBeCos += nSumSneg[5][0];
746                         dUdBeSin += nSumSneg[5][1];
747                         dUdGaCos += nSumSneg[6][0];
748                         dUdGaSin += nSumSneg[6][1];
749                     }
750                 }
751 
752                 // Assembly of potential derivatives componants
753                 dUda  += cosPhi * dUdaCos  + sinPhi * dUdaSin;
754                 dUdh  += cosPhi * dUdhCos  + sinPhi * dUdhSin;
755                 dUdk  += cosPhi * dUdkCos  + sinPhi * dUdkSin;
756                 dUdl  += cosPhi * dUdlCos  + sinPhi * dUdlSin;
757                 dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
758                 dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
759                 dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
760             }
761 
762             dUda  *= -moa / a;
763             dUdh  *=  moa;
764             dUdk  *=  moa;
765             dUdl  *=  moa;
766             dUdAl *=  moa;
767             dUdBe *=  moa;
768             dUdGa *=  moa;
769         }
770 
771         return new double[] {dUda, dUdh, dUdk, dUdl, dUdAl, dUdBe, dUdGa};
772     }
773 
774     /** Compute the n-SUM for potential derivatives components.
775      *  @param date current date
776      *  @param j resonant index <i>j</i>
777      *  @param m resonant order <i>m</i>
778      *  @param s d'Alembert characteristic <i>s</i>
779      *  @param maxN maximum possible value for <i>n</i> index
780      *  @param roaPow powers of R/a up to degree <i>n</i>
781      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
782      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
783      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
784      * @throws OrekitException if some error occurred
785      */
786     private double[][] computeNSum(final AbsoluteDate date,
787                                    final int j, final int m, final int s, final int maxN, final double[] roaPow,
788                                    final GHmsjPolynomials ghMSJ, final GammaMnsFunction gammaMNS)
789         throws OrekitException {
790 
791         //spherical harmonics
792         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
793 
794         // Potential derivatives components
795         double dUdaCos  = 0.;
796         double dUdaSin  = 0.;
797         double dUdhCos  = 0.;
798         double dUdhSin  = 0.;
799         double dUdkCos  = 0.;
800         double dUdkSin  = 0.;
801         double dUdlCos  = 0.;
802         double dUdlSin  = 0.;
803         double dUdAlCos = 0.;
804         double dUdAlSin = 0.;
805         double dUdBeCos = 0.;
806         double dUdBeSin = 0.;
807         double dUdGaCos = 0.;
808         double dUdGaSin = 0.;
809 
810         // I^m
811         final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);
812 
813         // jacobi v, w, indices from 2.7.1-(15)
814         final int v = FastMath.abs(m - s);
815         final int w = FastMath.abs(m + s);
816 
817         // Initialise lower degree nmin = (Max(2, m, |s|)) for summation over n
818         final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));
819 
820         //Get the corresponding Hansen object
821         final int sIndex = maxDegree + (j < 0 ? -s : s);
822         final int jIndex = FastMath.abs(j);
823         final HansenTesseralLinear hans = this.hansenObjects[sIndex][jIndex];
824 
825         // n-SUM from nmin to N
826         for (int n = nmin; n <= maxN; n++) {
827             // If (n - s) is odd, the contribution is null because of Vmns
828             if ((n - s) % 2 == 0) {
829 
830                 // Vmns coefficient
831                 final double fns    = fact[n + FastMath.abs(s)];
832                 final double vMNS   = CoefficientsFactory.getVmns(m, n, s, fns, fact[n - m]);
833 
834                 // Inclination function Gamma and derivative
835                 final double gaMNS  = gammaMNS.getValue(m, n, s);
836                 final double dGaMNS = gammaMNS.getDerivative(m, n, s);
837 
838                 // Hansen kernel value and derivative
839                 final double kJNS   = hans.getValue(-n - 1, chi);
840                 final double dkJNS  = hans.getDerivative(-n - 1, chi);
841 
842                 // Gjms, Hjms polynomials and derivatives
843                 final double gMSJ   = ghMSJ.getGmsj(m, s, j);
844                 final double hMSJ   = ghMSJ.getHmsj(m, s, j);
845                 final double dGdh   = ghMSJ.getdGmsdh(m, s, j);
846                 final double dGdk   = ghMSJ.getdGmsdk(m, s, j);
847                 final double dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
848                 final double dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
849                 final double dHdh   = ghMSJ.getdHmsdh(m, s, j);
850                 final double dHdk   = ghMSJ.getdHmsdk(m, s, j);
851                 final double dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
852                 final double dHdB   = ghMSJ.getdHmsdBeta(m, s, j);
853 
854                 // Jacobi l-index from 2.7.1-(15)
855                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
856                 // Jacobi polynomial and derivative
857                 final DerivativeStructure jacobi =
858                         JacobiPolynomials.getValue(l, v , w, new DerivativeStructure(1, 1, 0, gamma));
859 
860                 // Geopotential coefficients
861                 final double cnm = harmonics.getUnnormalizedCnm(n, m);
862                 final double snm = harmonics.getUnnormalizedSnm(n, m);
863 
864                 // Common factors from expansion of equations 3.3-4
865                 final double cf_0      = roaPow[n] * Im * vMNS;
866                 final double cf_1      = cf_0 * gaMNS * jacobi.getValue();
867                 final double cf_2      = cf_1 * kJNS;
868                 final double gcPhs     = gMSJ * cnm + hMSJ * snm;
869                 final double gsMhc     = gMSJ * snm - hMSJ * cnm;
870                 final double dKgcPhsx2 = 2. * dkJNS * gcPhs;
871                 final double dKgsMhcx2 = 2. * dkJNS * gsMhc;
872                 final double dUdaCoef  = (n + 1) * cf_2;
873                 final double dUdlCoef  = j * cf_2;
874                 final double dUdGaCoef = cf_0 * kJNS * (jacobi.getValue() * dGaMNS + gaMNS * jacobi.getPartialDerivative(1));
875 
876                 // dU / da components
877                 dUdaCos  += dUdaCoef * gcPhs;
878                 dUdaSin  += dUdaCoef * gsMhc;
879 
880                 // dU / dh components
881                 dUdhCos  += cf_1 * (kJNS * (cnm * dGdh + snm * dHdh) + h * dKgcPhsx2);
882                 dUdhSin  += cf_1 * (kJNS * (snm * dGdh - cnm * dHdh) + h * dKgsMhcx2);
883 
884                 // dU / dk components
885                 dUdkCos  += cf_1 * (kJNS * (cnm * dGdk + snm * dHdk) + k * dKgcPhsx2);
886                 dUdkSin  += cf_1 * (kJNS * (snm * dGdk - cnm * dHdk) + k * dKgsMhcx2);
887 
888                 // dU / dLambda components
889                 dUdlCos  +=  dUdlCoef * gsMhc;
890                 dUdlSin  += -dUdlCoef * gcPhs;
891 
892                 // dU / alpha components
893                 dUdAlCos += cf_2 * (dGdA * cnm + dHdA * snm);
894                 dUdAlSin += cf_2 * (dGdA * snm - dHdA * cnm);
895 
896                 // dU / dBeta components
897                 dUdBeCos += cf_2 * (dGdB * cnm + dHdB * snm);
898                 dUdBeSin += cf_2 * (dGdB * snm - dHdB * cnm);
899 
900                 // dU / dGamma components
901                 dUdGaCos += dUdGaCoef * gcPhs;
902                 dUdGaSin += dUdGaCoef * gsMhc;
903             }
904         }
905 
906         return new double[][] {{dUdaCos,  dUdaSin},
907                                {dUdhCos,  dUdhSin},
908                                {dUdkCos,  dUdkSin},
909                                {dUdlCos,  dUdlSin},
910                                {dUdAlCos, dUdAlSin},
911                                {dUdBeCos, dUdBeSin},
912                                {dUdGaCos, dUdGaSin}};
913     }
914 
915     /** {@inheritDoc} */
916     @Override
917     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
918         //nothing is done since this contribution is not sensitive to attitude
919     }
920 
921     /** {@inheritDoc} */
922     @Override
923     public void resetShortPeriodicsCoefficients() {
924         if (tesseralSPCoefs != null) {
925             tesseralSPCoefs.resetCoefficients();
926         }
927     }
928 
929     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
930      *  <p>
931      *  Those coefficients are given in Danielson paper by substituting the
932      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
933      *  </p>
934      */
935     private class FourierCjSjCoefficients {
936 
937         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
938         private final int jMax;
939 
940         /** The C<sub>i</sub><sup>jm</sup> coefficients.
941          * <p>
942          * The index order is [m][j][i] <br/>
943          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
944          * compute the following: <br/>
945          * - da/dt <br/>
946          * - dk/dt <br/>
947          * - dh/dt / dk <br/>
948          * - dq/dt <br/>
949          * - dp/dt / dα <br/>
950          * - dλ/dt / dβ <br/>
951          * </p>
952          */
953         private final double[][][] cCoef;
954 
955         /** The S<sub>i</sub><sup>jm</sup> coefficients.
956          * <p>
957          * The index order is [m][j][i] <br/>
958          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
959          * compute the following: <br/>
960          * - da/dt <br/>
961          * - dk/dt <br/>
962          * - dh/dt / dk <br/>
963          * - dq/dt <br/>
964          * - dp/dt / dα <br/>
965          * - dλ/dt / dβ <br/>
966          * </p>
967          */
968         private final double[][][] sCoef;
969 
970         /** G<sub>ms</sub><sup>j</sup> and H<sub>ms</sub><sup>j</sup> polynomials. */
971         private GHmsjPolynomials ghMSJ;
972 
973         /** &Gamma;<sub>ns</sub><sup>m</sup> function. */
974         private GammaMnsFunction gammaMNS;
975 
976         /** R / a up to power degree. */
977         private final double[] roaPow;
978 
979         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
980          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
981          *  @param mMax maximum value for m
982          */
983         public FourierCjSjCoefficients(final int jMax, final int mMax) {
984             // initialise fields
985             this.jMax = jMax;
986             this.cCoef = new double[mMax + 1][2 * jMax + 1][6];
987             this.sCoef = new double[mMax + 1][2 * jMax + 1][6];
988             this.roaPow = new double[maxDegree + 1];
989             roaPow[0] = 1.;
990         }
991 
992         /**
993          * Generate the coefficients.
994          * @param date the current date
995          * @throws OrekitException if an error occurs while generating the coefficients
996          */
997         public void generateCoefficients(final AbsoluteDate date) throws OrekitException {
998             // Compute only if there is at least one non-resonant tesseral
999             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1000                 // Gmsj and Hmsj polynomials
1001                 ghMSJ = new GHmsjPolynomials(k, h, alpha, beta, I);
1002 
1003                 // GAMMAmns function
1004                 gammaMNS = new GammaMnsFunction(fact, gamma, I);
1005 
1006                 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);
1007 
1008                 // R / a up to power degree
1009                 for (int i = 1; i <= maxRoaPower; i++) {
1010                     roaPow[i] = roa * roaPow[i - 1];
1011                 }
1012 
1013                 //generate the m-daily coefficients
1014                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1015                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP);
1016                 }
1017 
1018                 // generate the other coefficients only if required
1019                 if (!mDailiesOnly) {
1020                     for (int m: nonResOrders.keySet()) {
1021                         final List<Integer> listJ = nonResOrders.get(m);
1022 
1023                         for (int j: listJ) {
1024 
1025                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP);
1026                         }
1027                     }
1028                 }
1029             }
1030         }
1031 
1032         /** Build a set of fourier coefficients for a given m and j.
1033          *
1034          * @param date the date of the coefficients
1035          * @param m m index
1036          * @param j j index
1037          * @param maxN  maximum value for n index
1038          * @throws OrekitException in case of Hansen kernel generation error
1039          */
1040         private void buildFourierCoefficients(final AbsoluteDate date,
1041                final int m, final int j, final int maxN) throws OrekitException {
1042             // Potential derivatives components for a given non-resonant pair {j,m}
1043             double dRdaCos  = 0.;
1044             double dRdaSin  = 0.;
1045             double dRdhCos  = 0.;
1046             double dRdhSin  = 0.;
1047             double dRdkCos  = 0.;
1048             double dRdkSin  = 0.;
1049             double dRdlCos  = 0.;
1050             double dRdlSin  = 0.;
1051             double dRdAlCos = 0.;
1052             double dRdAlSin = 0.;
1053             double dRdBeCos = 0.;
1054             double dRdBeSin = 0.;
1055             double dRdGaCos = 0.;
1056             double dRdGaSin = 0.;
1057 
1058             // s-SUM from -sMin to sMax
1059             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1060             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1061             for (int s = 0; s <= sMax; s++) {
1062 
1063                 // n-SUM for s positive
1064                 final double[][] nSumSpos = computeNSum(date, j, m, s, maxN,
1065                                                         roaPow, ghMSJ, gammaMNS);
1066                 dRdaCos  += nSumSpos[0][0];
1067                 dRdaSin  += nSumSpos[0][1];
1068                 dRdhCos  += nSumSpos[1][0];
1069                 dRdhSin  += nSumSpos[1][1];
1070                 dRdkCos  += nSumSpos[2][0];
1071                 dRdkSin  += nSumSpos[2][1];
1072                 dRdlCos  += nSumSpos[3][0];
1073                 dRdlSin  += nSumSpos[3][1];
1074                 dRdAlCos += nSumSpos[4][0];
1075                 dRdAlSin += nSumSpos[4][1];
1076                 dRdBeCos += nSumSpos[5][0];
1077                 dRdBeSin += nSumSpos[5][1];
1078                 dRdGaCos += nSumSpos[6][0];
1079                 dRdGaSin += nSumSpos[6][1];
1080 
1081                 // n-SUM for s negative
1082                 if (s > 0 && s <= sMin) {
1083                     final double[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
1084                                                             roaPow, ghMSJ, gammaMNS);
1085                     dRdaCos  += nSumSneg[0][0];
1086                     dRdaSin  += nSumSneg[0][1];
1087                     dRdhCos  += nSumSneg[1][0];
1088                     dRdhSin  += nSumSneg[1][1];
1089                     dRdkCos  += nSumSneg[2][0];
1090                     dRdkSin  += nSumSneg[2][1];
1091                     dRdlCos  += nSumSneg[3][0];
1092                     dRdlSin  += nSumSneg[3][1];
1093                     dRdAlCos += nSumSneg[4][0];
1094                     dRdAlSin += nSumSneg[4][1];
1095                     dRdBeCos += nSumSneg[5][0];
1096                     dRdBeSin += nSumSneg[5][1];
1097                     dRdGaCos += nSumSneg[6][0];
1098                     dRdGaSin += nSumSneg[6][1];
1099                 }
1100             }
1101             dRdaCos  *= -moa / a;
1102             dRdaSin  *= -moa / a;
1103             dRdhCos  *=  moa;
1104             dRdhSin  *=  moa;
1105             dRdkCos  *=  moa;
1106             dRdkSin *=  moa;
1107             dRdlCos *=  moa;
1108             dRdlSin *=  moa;
1109             dRdAlCos *=  moa;
1110             dRdAlSin *=  moa;
1111             dRdBeCos *=  moa;
1112             dRdBeSin *=  moa;
1113             dRdGaCos *=  moa;
1114             dRdGaSin *=  moa;
1115 
1116             // Compute the cross derivative operator :
1117             final double RAlphaGammaCos   = alpha * dRdGaCos - gamma * dRdAlCos;
1118             final double RAlphaGammaSin   = alpha * dRdGaSin - gamma * dRdAlSin;
1119             final double RAlphaBetaCos    = alpha * dRdBeCos - beta  * dRdAlCos;
1120             final double RAlphaBetaSin    = alpha * dRdBeSin - beta  * dRdAlSin;
1121             final double RBetaGammaCos    =  beta * dRdGaCos - gamma * dRdBeCos;
1122             final double RBetaGammaSin    =  beta * dRdGaSin - gamma * dRdBeSin;
1123             final double RhkCos           =     h * dRdkCos  -     k * dRdhCos;
1124             final double RhkSin           =     h * dRdkSin  -     k * dRdhSin;
1125             final double pRagmIqRbgoABCos = (p * RAlphaGammaCos - I * q * RBetaGammaCos) * ooAB;
1126             final double pRagmIqRbgoABSin = (p * RAlphaGammaSin - I * q * RBetaGammaSin) * ooAB;
1127             final double RhkmRabmdRdlCos  =  RhkCos - RAlphaBetaCos - dRdlCos;
1128             final double RhkmRabmdRdlSin  =  RhkSin - RAlphaBetaSin - dRdlSin;
1129 
1130             // da/dt
1131             cCoef[m][j + jMax][0] = ax2oA * dRdlCos;
1132             sCoef[m][j + jMax][0] = ax2oA * dRdlSin;
1133 
1134             // dk/dt
1135             cCoef[m][j + jMax][1] = -(BoA * dRdhCos + h * pRagmIqRbgoABCos + k * BoABpo * dRdlCos);
1136             sCoef[m][j + jMax][1] = -(BoA * dRdhSin + h * pRagmIqRbgoABSin + k * BoABpo * dRdlSin);
1137 
1138             // dh/dt
1139             cCoef[m][j + jMax][2] = BoA * dRdkCos + k * pRagmIqRbgoABCos - h * BoABpo * dRdlCos;
1140             sCoef[m][j + jMax][2] = BoA * dRdkSin + k * pRagmIqRbgoABSin - h * BoABpo * dRdlSin;
1141 
1142             // dq/dt
1143             cCoef[m][j + jMax][3] = Co2AB * (q * RhkmRabmdRdlCos - I * RAlphaGammaCos);
1144             sCoef[m][j + jMax][3] = Co2AB * (q * RhkmRabmdRdlSin - I * RAlphaGammaSin);
1145 
1146             // dp/dt
1147             cCoef[m][j + jMax][4] = Co2AB * (p * RhkmRabmdRdlCos - RBetaGammaCos);
1148             sCoef[m][j + jMax][4] = Co2AB * (p * RhkmRabmdRdlSin - RBetaGammaSin);
1149 
1150             // dλ/dt
1151             cCoef[m][j + jMax][5] = -ax2oA * dRdaCos + BoABpo * (h * dRdhCos + k * dRdkCos) + pRagmIqRbgoABCos;
1152             sCoef[m][j + jMax][5] = -ax2oA * dRdaSin + BoABpo * (h * dRdhSin + k * dRdkSin) + pRagmIqRbgoABSin;
1153         }
1154 
1155         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
1156          * @param i i index - corresponds to the required variation
1157          * @param j j index
1158          * @param m m index
1159          * @return the coefficient C<sub>i</sub><sup>jm</sup>
1160          */
1161         public double getCijm(final int i, final int j, final int m) {
1162             return cCoef[m][j + jMax][i];
1163         }
1164 
1165         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
1166          * @param i i index - corresponds to the required variation
1167          * @param j j index
1168          * @param m m index
1169          * @return the coefficient S<sub>i</sub><sup>jm</sup>
1170          */
1171         public double getSijm(final int i, final int j, final int m) {
1172             return sCoef[m][j + jMax][i];
1173         }
1174     }
1175 
1176     /** The C<sup>i</sup><sub>m</sub><sub>t</sub> and S<sup>i</sup><sub>m</sub><sub>t</sub> coefficients used to compute
1177      * the short-periodic zonal contribution.
1178      *   <p>
1179      *  Those coefficients are given by expression 2.5.4-5 from the Danielsno paper.
1180      *   </p>
1181      *
1182      * @author Sorin Scortan
1183      *
1184      */
1185     private class TesseralShortPeriodicCoefficients {
1186 
1187         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
1188          * <p>
1189          * The index order is cijm[m][j][i] <br/>
1190          * i corresponds to the equinoctial element, as follows: <br/>
1191          * - i=0 for a <br/>
1192          * - i=1 for k <br/>
1193          * - i=2 for h <br/>
1194          * - i=3 for q <br/>
1195          * - i=4 for p <br/>
1196          * - i=5 for λ <br/>
1197          * </p>
1198          */
1199         private final ShortPeriodicsInterpolatedCoefficient[][][] cijm;
1200 
1201         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
1202          * <p>
1203          * The index order is sijm[m][j][i] <br/>
1204          * i corresponds to the equinoctial element, as follows: <br/>
1205          * - i=0 for a <br/>
1206          * - i=1 for k <br/>
1207          * - i=2 for h <br/>
1208          * - i=3 for q <br/>
1209          * - i=4 for p <br/>
1210          * - i=5 for λ <br/>
1211          * </p>
1212          */
1213         private final ShortPeriodicsInterpolatedCoefficient[][][] sijm;
1214 
1215         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
1216         private final int jMax;
1217 
1218         /** Maximum value for m.  */
1219         private final int mMax;
1220 
1221         /** The fourier coefficients. */
1222         private final FourierCjSjCoefficients cjsjFourier;
1223 
1224         /** 3n / 2a. */
1225         private double tnota;
1226 
1227         /** Constructor.
1228          * @param jMax absolute limit for j ( -jMax <= j <= jMax )
1229          * @param mMax maximum value for m
1230          * @param interpolationPoints number of points used in the interpolation process
1231          */
1232         public TesseralShortPeriodicCoefficients(final int jMax, final int mMax, final int interpolationPoints) {
1233 
1234             // Initialize fields
1235             this.jMax = jMax;
1236             this.mMax = mMax;
1237             this.cijm = new ShortPeriodicsInterpolatedCoefficient[mMax + 1][2 * jMax + 1][6];
1238             this.sijm = new ShortPeriodicsInterpolatedCoefficient[mMax + 1][2 * jMax + 1][6];
1239             this.cjsjFourier = new FourierCjSjCoefficients(jMax, mMax);
1240 
1241             for (int m = 1; m <= mMax; m++) {
1242                 for (int j = -jMax; j <= jMax; j++) {
1243                     for (int i = 0; i < 6; i++) {
1244                         this.cijm[m][j + jMax][i] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
1245                         this.sijm[m][j + jMax][i] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
1246                     }
1247                 }
1248             }
1249         }
1250 
1251         /** Compute the short periodic coefficients.
1252          *
1253          * @param date the current date
1254          * @throws OrekitException if an error occurs
1255          */
1256         public void computeCoefficients(final AbsoluteDate date)
1257             throws OrekitException {
1258             // Compute only if there is at least one non-resonant tesseral
1259             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1260                 // Generate the fourrier coefficients
1261                 cjsjFourier.generateCoefficients(date);
1262 
1263                 // the coefficient 3n / 2a
1264                 tnota = 1.5 * meanMotion / a;
1265 
1266                 // build the mDaily coefficients
1267                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1268                     // build the coefficients
1269                     buildCoefficients(date, m, 0);
1270                 }
1271 
1272                 if (!mDailiesOnly) {
1273                     // generate the other coefficients, if required
1274                     for (int m: nonResOrders.keySet()) {
1275                         final List<Integer> listJ = nonResOrders.get(m);
1276 
1277                         for (int j: listJ) {
1278                             // build the coefficients
1279                             buildCoefficients(date, m, j);
1280                         }
1281                     }
1282                 }
1283             }
1284 
1285         }
1286 
1287         /** Build a set of coefficients.
1288          *
1289          * @param date the current date
1290          * @param m m index
1291          * @param j j index
1292          */
1293         private void buildCoefficients(final AbsoluteDate date, final int m, final int j) {
1294             // Create local arrays
1295             final double[] currentCijm = new double[] {0., 0., 0., 0., 0., 0.};
1296             final double[] currentSijm = new double[] {0., 0., 0., 0., 0., 0.};
1297 
1298             // compute the term 1 / (jn - mθ<sup>.</sup>)
1299             final double oojnmt = 1. / (j * meanMotion - m * centralBodyRotationRate);
1300 
1301             // initialise the coeficients
1302             for (int i = 0; i < 6; i++) {
1303                 currentCijm[i] = -cjsjFourier.getSijm(i, j, m);
1304                 currentSijm[i] = cjsjFourier.getCijm(i, j, m);
1305             }
1306             // Add the separate part for δ<sub>6i</sub>
1307             currentCijm[5] += tnota * oojnmt * cjsjFourier.getCijm(0, j, m);
1308             currentSijm[5] += tnota * oojnmt * cjsjFourier.getSijm(0, j, m);
1309 
1310             //Multiply by 1 / (jn - mθ<sup>.</sup>)
1311             for (int i = 0; i < 6; i++) {
1312                 currentCijm[i] *= oojnmt;
1313                 currentSijm[i] *= oojnmt;
1314             }
1315 
1316             // Add the coefficients to the interpolation grid
1317             for (int i = 0; i < 6; i++) {
1318                 cijm[m][j + jMax][i].addGridPoint(date, currentCijm[i]);
1319                 sijm[m][j + jMax][i].addGridPoint(date, currentSijm[i]);
1320             }
1321         }
1322 
1323         /** Reset the coefficients.
1324          * <p>
1325          * For each coefficient, clear history of computed points
1326          * </p>
1327          */
1328         public void resetCoefficients() {
1329             for (int m = 1; m <= mMax; m++) {
1330                 for (int j = -jMax; j <= jMax; j++) {
1331                     for (int i = 0; i < 6; i++) {
1332                         this.cijm[m][j + jMax][i].clearHistory();
1333                         this.sijm[m][j + jMax][i].clearHistory();
1334                     }
1335                 }
1336             }
1337         }
1338 
1339         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
1340          *
1341          * @param i i index
1342          * @param j j index
1343          * @param m m index
1344          * @param date the date
1345          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
1346          */
1347         public double getCijm(final int i, final int j, final int m, final AbsoluteDate date) {
1348             return cijm[m][j + jMax][i].value(date);
1349         }
1350 
1351         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
1352          *
1353          * @param i i index
1354          * @param j j index
1355          * @param m m index
1356          * @param date the date
1357          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
1358          */
1359         public double getSijm(final int i, final int j, final int m, final AbsoluteDate date) {
1360             return sijm[m][j + jMax][i].value(date);
1361         }
1362     }
1363 }