1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.propagation.semianalytical.dsst.forces;
18
19 import java.io.NotSerializableException;
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.SortedMap;
28 import java.util.SortedSet;
29 import java.util.TreeMap;
30
31 import org.hipparchus.analysis.differentiation.DSFactory;
32 import org.hipparchus.analysis.differentiation.DerivativeStructure;
33 import org.hipparchus.exception.LocalizedCoreFormats;
34 import org.hipparchus.geometry.euclidean.threed.Vector3D;
35 import org.hipparchus.util.FastMath;
36 import org.hipparchus.util.MathUtils;
37 import org.orekit.attitudes.AttitudeProvider;
38 import org.orekit.errors.OrekitException;
39 import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
40 import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
41 import org.orekit.frames.Frame;
42 import org.orekit.frames.Transform;
43 import org.orekit.orbits.Orbit;
44 import org.orekit.propagation.SpacecraftState;
45 import org.orekit.propagation.events.EventDetector;
46 import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
47 import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
48 import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
49 import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
50 import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
51 import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
52 import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenTesseralLinear;
53 import org.orekit.time.AbsoluteDate;
54 import org.orekit.utils.TimeSpanMap;
55
56
57
58
59
60
61
62
63
64 public class DSSTTesseral implements DSSTForceModel {
65
66
67
68
69 private static final double MIN_PERIOD_IN_SECONDS = 864000.;
70
71
72
73
74 private static final double MIN_PERIOD_IN_SAT_REV = 10.;
75
76
77 private static final int INTERPOLATION_POINTS = 3;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 private static final int I = 1;
93
94
95 private final UnnormalizedSphericalHarmonicsProvider provider;
96
97
98 private final Frame bodyFrame;
99
100
101 private final double centralBodyRotationRate;
102
103
104 private final double bodyPeriod;
105
106
107 private final int maxDegree;
108
109
110 private final int maxDegreeTesseralSP;
111
112
113 private final int maxDegreeMdailyTesseralSP;
114
115
116 private final int maxOrder;
117
118
119 private final int maxOrderTesseralSP;
120
121
122 private final int maxOrderMdailyTesseralSP;
123
124
125 private final List<Integer> resOrders;
126
127
128 private int maxEccPow;
129
130
131
132 private final int maxEccPowTesseralSP;
133
134
135
136 private final int maxEccPowMdailyTesseralSP;
137
138
139 private final int maxFrequencyShortPeriodics;
140
141
142 private int maxHansen;
143
144
145 private double orbitPeriod;
146
147
148 private double ratio;
149
150
151
152 private double a;
153
154
155 private double k;
156
157
158 private double h;
159
160
161 private double q;
162
163
164 private double p;
165
166
167 private double lm;
168
169
170 private double ecc;
171
172
173
174 private double chi;
175
176
177 private double chi2;
178
179
180
181 private Vector3D f;
182
183
184 private Vector3D g;
185
186
187 private double theta;
188
189
190 private double alpha;
191
192
193 private double beta;
194
195
196 private double gamma;
197
198
199
200 private double ax2oA;
201
202
203 private double ooAB;
204
205
206 private double BoA;
207
208
209 private double BoABpo;
210
211
212 private double Co2AB;
213
214
215 private double moa;
216
217
218 private double roa;
219
220
221 private double e2;
222
223
224 private double meanMotion;
225
226
227 private final SortedMap<Integer, List<Integer> > nonResOrders;
228
229
230
231 private HansenTesseralLinear[][] hansenObjects;
232
233
234 private FourierCjSjCoefficients cjsjFourier;
235
236
237 private TesseralShortPeriodicCoefficients shortPeriodTerms;
238
239
240 private final DSFactory factory;
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 public DSSTTesseral(final Frame centralBodyFrame,
266 final double centralBodyRotationRate,
267 final UnnormalizedSphericalHarmonicsProvider provider,
268 final int maxDegreeTesseralSP, final int maxOrderTesseralSP,
269 final int maxEccPowTesseralSP, final int maxFrequencyShortPeriodics,
270 final int maxDegreeMdailyTesseralSP, final int maxOrderMdailyTesseralSP,
271 final int maxEccPowMdailyTesseralSP)
272 throws OrekitException {
273
274
275 this.bodyFrame = centralBodyFrame;
276
277
278 this.centralBodyRotationRate = centralBodyRotationRate;
279
280
281 this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;
282
283
284 this.provider = provider;
285 this.maxDegree = provider.getMaxDegree();
286 this.maxOrder = provider.getMaxOrder();
287
288
289 checkIndexRange(maxDegreeTesseralSP, 2, maxDegree);
290 this.maxDegreeTesseralSP = maxDegreeTesseralSP;
291
292 checkIndexRange(maxDegreeMdailyTesseralSP, 2, maxDegree);
293 this.maxDegreeMdailyTesseralSP = maxDegreeMdailyTesseralSP;
294
295 checkIndexRange(maxOrderTesseralSP, 0, maxOrder);
296 this.maxOrderTesseralSP = maxOrderTesseralSP;
297
298 checkIndexRange(maxOrderMdailyTesseralSP, 0, maxOrder);
299 this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
300
301
302 this.maxEccPowTesseralSP = maxEccPowTesseralSP;
303
304 checkIndexRange(maxEccPowMdailyTesseralSP, 0, maxDegreeMdailyTesseralSP - 2);
305 this.maxEccPowMdailyTesseralSP = maxEccPowMdailyTesseralSP;
306
307
308 this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
309
310
311 this.resOrders = new ArrayList<Integer>();
312 this.nonResOrders = new TreeMap<Integer, List <Integer> >();
313 this.maxEccPow = 0;
314 this.maxHansen = 0;
315
316 this.factory = new DSFactory(1, 1);
317
318 }
319
320
321
322
323
324
325
326 private void checkIndexRange(final int index, final int min, final int max)
327 throws OrekitException {
328 if (index < min || index > max) {
329 throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
330 }
331 }
332
333
334 @Override
335 public List<ShortPeriodTerms> initialize(final AuxiliaryElements aux, final boolean meanOnly)
336 throws OrekitException {
337
338
339 orbitPeriod = aux.getKeplerianPeriod();
340
341
342
343
344 final double e = aux.getEcc();
345 if (e <= 0.005) {
346 maxEccPow = 3;
347 } else if (e <= 0.02) {
348 maxEccPow = 4;
349 } else if (e <= 0.1) {
350 maxEccPow = 7;
351 } else if (e <= 0.2) {
352 maxEccPow = 10;
353 } else if (e <= 0.3) {
354 maxEccPow = 12;
355 } else if (e <= 0.4) {
356 maxEccPow = 15;
357 } else {
358 maxEccPow = 20;
359 }
360
361
362 maxHansen = maxEccPow / 2;
363
364
365 ratio = orbitPeriod / bodyPeriod;
366
367
368 getResonantAndNonResonantTerms(meanOnly);
369
370
371 createHansenObjects(meanOnly);
372
373 final int mMax = FastMath.max(maxOrderTesseralSP, maxOrderMdailyTesseralSP);
374 cjsjFourier = new FourierCjSjCoefficients(maxFrequencyShortPeriodics, mMax);
375
376 shortPeriodTerms = new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP,
377 maxDegreeTesseralSP < 0, nonResOrders,
378 mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
379 new TimeSpanMap<Slot>(new Slot(mMax, maxFrequencyShortPeriodics,
380 INTERPOLATION_POINTS)));
381
382 final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
383 list.add(shortPeriodTerms);
384 return list;
385
386 }
387
388
389
390
391
392
393
394
395
396
397
398
399
400 private void createHansenObjects(final boolean meanOnly) {
401
402 final int rows = 2 * maxDegree + 1;
403 final int columns = maxFrequencyShortPeriodics + 1;
404 this.hansenObjects = new HansenTesseralLinear[rows][columns];
405
406 if (meanOnly) {
407
408 for (int m : resOrders) {
409
410 final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
411
412
413 final int sMin = FastMath.min(maxEccPow - j, maxDegree);
414 final int sMax = FastMath.min(maxEccPow + j, maxDegree);
415
416
417 for (int s = 0; s <= sMax; s++) {
418
419 final int n0 = FastMath.max(FastMath.max(2, m), s);
420
421
422 this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
423
424 if (s > 0 && s <= sMin) {
425
426 this.hansenObjects[maxDegree - s][j] = new HansenTesseralLinear(maxDegree, -s, j, n0, maxHansen);
427 }
428 }
429 }
430 } else {
431
432 for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
433 for (int s = -maxDegree; s <= maxDegree; s++) {
434
435 final int n0 = FastMath.max(2, FastMath.abs(s));
436
437 this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
438 }
439 }
440 }
441 }
442
443
444 @Override
445 public void initializeStep(final AuxiliaryElements aux) throws OrekitException {
446
447
448 a = aux.getSma();
449 k = aux.getK();
450 h = aux.getH();
451 q = aux.getQ();
452 p = aux.getP();
453 lm = aux.getLM();
454
455
456 ecc = aux.getEcc();
457 e2 = ecc * ecc;
458
459
460 f = aux.getVectorF();
461 g = aux.getVectorG();
462
463
464 final Transform t = bodyFrame.getTransformTo(aux.getFrame(), aux.getDate());
465 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
466 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
467 theta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
468 f.dotProduct(xB) + I * g.dotProduct(yB));
469
470
471 alpha = aux.getAlpha();
472 beta = aux.getBeta();
473 gamma = aux.getGamma();
474
475
476
477 final double A = aux.getA();
478
479 final double B = aux.getB();
480
481 final double C = aux.getC();
482
483
484 ax2oA = 2. * a / A;
485
486 BoA = B / A;
487
488 ooAB = 1. / (A * B);
489
490 Co2AB = C * ooAB / 2.;
491
492 BoABpo = BoA / (1. + B);
493
494 moa = provider.getMu() / a;
495
496 roa = provider.getAe() / a;
497
498
499 chi = 1. / B;
500 chi2 = chi * chi;
501
502
503 meanMotion = aux.getMeanMotion();
504 }
505
506
507 @Override
508 public double[] getMeanElementRate(final SpacecraftState spacecraftState) throws OrekitException {
509
510
511 final double[] dU = computeUDerivatives(spacecraftState.getDate());
512 final double dUda = dU[0];
513 final double dUdh = dU[1];
514 final double dUdk = dU[2];
515 final double dUdl = dU[3];
516 final double dUdAl = dU[4];
517 final double dUdBe = dU[5];
518 final double dUdGa = dU[6];
519
520
521 final double UAlphaGamma = alpha * dUdGa - gamma * dUdAl;
522 final double UAlphaBeta = alpha * dUdBe - beta * dUdAl;
523 final double UBetaGamma = beta * dUdGa - gamma * dUdBe;
524 final double Uhk = h * dUdk - k * dUdh;
525 final double pUagmIqUbgoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;
526 final double UhkmUabmdUdl = Uhk - UAlphaBeta - dUdl;
527
528 final double da = ax2oA * dUdl;
529 final double dh = BoA * dUdk + k * pUagmIqUbgoAB - h * BoABpo * dUdl;
530 final double dk = -(BoA * dUdh + h * pUagmIqUbgoAB + k * BoABpo * dUdl);
531 final double dp = Co2AB * (p * UhkmUabmdUdl - UBetaGamma);
532 final double dq = Co2AB * (q * UhkmUabmdUdl - I * UAlphaGamma);
533 final double dM = -ax2oA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUagmIqUbgoAB;
534
535 return new double[] {da, dk, dh, dq, dp, dM};
536 }
537
538
539 @Override
540 public void updateShortPeriodTerms(final SpacecraftState... meanStates)
541 throws OrekitException {
542
543 final Slot slot = shortPeriodTerms.createSlot(meanStates);
544
545 for (final SpacecraftState meanState : meanStates) {
546
547 initializeStep(new AuxiliaryElements(meanState.getOrbit(), I));
548
549
550 for (int s = -maxDegree; s <= maxDegree; s++) {
551
552 this.hansenObjects[s + maxDegree][0].computeInitValues(e2, chi, chi2);
553 if (maxDegreeTesseralSP >= 0) {
554
555 for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
556 this.hansenObjects[s + maxDegree][j].computeInitValues(e2, chi, chi2);
557 }
558 }
559 }
560
561
562
563 if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
564
565 cjsjFourier.generateCoefficients(meanState.getDate());
566
567
568 final double tnota = 1.5 * meanMotion / a;
569
570
571 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
572
573 buildCoefficients(meanState.getDate(), slot, m, 0, tnota);
574 }
575
576 if (maxDegreeTesseralSP >= 0) {
577
578 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
579
580 for (int j : entry.getValue()) {
581
582 buildCoefficients(meanState.getDate(), slot, entry.getKey(), j, tnota);
583 }
584 }
585 }
586 }
587
588 }
589
590 }
591
592
593
594
595
596
597
598
599
600 private void buildCoefficients(final AbsoluteDate date, final Slot slot,
601 final int m, final int j, final double tnota) {
602
603 final double[] currentCijm = new double[] {0., 0., 0., 0., 0., 0.};
604 final double[] currentSijm = new double[] {0., 0., 0., 0., 0., 0.};
605
606
607 final double oojnmt = 1. / (j * meanMotion - m * centralBodyRotationRate);
608
609
610 for (int i = 0; i < 6; i++) {
611 currentCijm[i] = -cjsjFourier.getSijm(i, j, m);
612 currentSijm[i] = cjsjFourier.getCijm(i, j, m);
613 }
614
615 currentCijm[5] += tnota * oojnmt * cjsjFourier.getCijm(0, j, m);
616 currentSijm[5] += tnota * oojnmt * cjsjFourier.getSijm(0, j, m);
617
618
619 for (int i = 0; i < 6; i++) {
620 currentCijm[i] *= oojnmt;
621 currentSijm[i] *= oojnmt;
622 }
623
624
625 slot.cijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentCijm);
626 slot.sijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentSijm);
627
628 }
629
630
631 @Override
632 public EventDetector[] getEventsDetectors() {
633 return null;
634 }
635
636
637
638
639
640
641 private void getResonantAndNonResonantTerms(final boolean resonantOnly) {
642
643
644 final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
645 MIN_PERIOD_IN_SECONDS / orbitPeriod);
646
647
648 resOrders.clear();
649 nonResOrders.clear();
650 for (int m = 1; m <= maxOrder; m++) {
651 final double resonance = ratio * m;
652 int jRes = 0;
653 final int jComputedRes = (int) FastMath.round(resonance);
654 if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance - jComputedRes) <= tolerance) {
655
656 resOrders.add(m);
657 jRes = jComputedRes;
658 }
659
660 if (!resonantOnly && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) {
661
662 final List<Integer> listJofM = new ArrayList<Integer>();
663
664 for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) {
665 if (j != 0 && j != jRes) {
666 listJofM.add(j);
667 }
668 }
669
670 nonResOrders.put(m, listJofM);
671 }
672 }
673 }
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692 private double[] computeUDerivatives(final AbsoluteDate date) throws OrekitException {
693
694
695 double dUda = 0.;
696 double dUdh = 0.;
697 double dUdk = 0.;
698 double dUdl = 0.;
699 double dUdAl = 0.;
700 double dUdBe = 0.;
701 double dUdGa = 0.;
702
703
704 if (!resOrders.isEmpty()) {
705
706 final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(k, h, alpha, beta, I);
707
708
709 final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, gamma, I);
710
711
712 final double[] roaPow = new double[maxDegree + 1];
713 roaPow[0] = 1.;
714 for (int i = 1; i <= maxDegree; i++) {
715 roaPow[i] = roa * roaPow[i - 1];
716 }
717
718
719 for (int m : resOrders) {
720
721
722 final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
723
724
725 final double jlMmt = j * lm - m * theta;
726 final double sinPhi = FastMath.sin(jlMmt);
727 final double cosPhi = FastMath.cos(jlMmt);
728
729
730 double dUdaCos = 0.;
731 double dUdaSin = 0.;
732 double dUdhCos = 0.;
733 double dUdhSin = 0.;
734 double dUdkCos = 0.;
735 double dUdkSin = 0.;
736 double dUdlCos = 0.;
737 double dUdlSin = 0.;
738 double dUdAlCos = 0.;
739 double dUdAlSin = 0.;
740 double dUdBeCos = 0.;
741 double dUdBeSin = 0.;
742 double dUdGaCos = 0.;
743 double dUdGaSin = 0.;
744
745
746 final int sMin = FastMath.min(maxEccPow - j, maxDegree);
747 final int sMax = FastMath.min(maxEccPow + j, maxDegree);
748 for (int s = 0; s <= sMax; s++) {
749
750
751 this.hansenObjects[s + maxDegree][j].computeInitValues(e2, chi, chi2);
752
753
754 final double[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
755 roaPow, ghMSJ, gammaMNS);
756 dUdaCos += nSumSpos[0][0];
757 dUdaSin += nSumSpos[0][1];
758 dUdhCos += nSumSpos[1][0];
759 dUdhSin += nSumSpos[1][1];
760 dUdkCos += nSumSpos[2][0];
761 dUdkSin += nSumSpos[2][1];
762 dUdlCos += nSumSpos[3][0];
763 dUdlSin += nSumSpos[3][1];
764 dUdAlCos += nSumSpos[4][0];
765 dUdAlSin += nSumSpos[4][1];
766 dUdBeCos += nSumSpos[5][0];
767 dUdBeSin += nSumSpos[5][1];
768 dUdGaCos += nSumSpos[6][0];
769 dUdGaSin += nSumSpos[6][1];
770
771
772 if (s > 0 && s <= sMin) {
773
774 this.hansenObjects[maxDegree - s][j].computeInitValues(e2, chi, chi2);
775
776 final double[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
777 roaPow, ghMSJ, gammaMNS);
778 dUdaCos += nSumSneg[0][0];
779 dUdaSin += nSumSneg[0][1];
780 dUdhCos += nSumSneg[1][0];
781 dUdhSin += nSumSneg[1][1];
782 dUdkCos += nSumSneg[2][0];
783 dUdkSin += nSumSneg[2][1];
784 dUdlCos += nSumSneg[3][0];
785 dUdlSin += nSumSneg[3][1];
786 dUdAlCos += nSumSneg[4][0];
787 dUdAlSin += nSumSneg[4][1];
788 dUdBeCos += nSumSneg[5][0];
789 dUdBeSin += nSumSneg[5][1];
790 dUdGaCos += nSumSneg[6][0];
791 dUdGaSin += nSumSneg[6][1];
792 }
793 }
794
795
796 dUda += cosPhi * dUdaCos + sinPhi * dUdaSin;
797 dUdh += cosPhi * dUdhCos + sinPhi * dUdhSin;
798 dUdk += cosPhi * dUdkCos + sinPhi * dUdkSin;
799 dUdl += cosPhi * dUdlCos + sinPhi * dUdlSin;
800 dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
801 dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
802 dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
803 }
804
805 dUda *= -moa / a;
806 dUdh *= moa;
807 dUdk *= moa;
808 dUdl *= moa;
809 dUdAl *= moa;
810 dUdBe *= moa;
811 dUdGa *= moa;
812 }
813
814 return new double[] {dUda, dUdh, dUdk, dUdl, dUdAl, dUdBe, dUdGa};
815 }
816
817
818
819
820
821
822
823
824
825
826
827
828
829 private double[][] computeNSum(final AbsoluteDate date,
830 final int j, final int m, final int s, final int maxN, final double[] roaPow,
831 final GHmsjPolynomials ghMSJ, final GammaMnsFunction gammaMNS)
832 throws OrekitException {
833
834
835 final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
836
837
838 double dUdaCos = 0.;
839 double dUdaSin = 0.;
840 double dUdhCos = 0.;
841 double dUdhSin = 0.;
842 double dUdkCos = 0.;
843 double dUdkSin = 0.;
844 double dUdlCos = 0.;
845 double dUdlSin = 0.;
846 double dUdAlCos = 0.;
847 double dUdAlSin = 0.;
848 double dUdBeCos = 0.;
849 double dUdBeSin = 0.;
850 double dUdGaCos = 0.;
851 double dUdGaSin = 0.;
852
853
854 @SuppressWarnings("unused")
855 final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);
856
857
858 final int v = FastMath.abs(m - s);
859 final int w = FastMath.abs(m + s);
860
861
862 final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));
863
864
865 final int sIndex = maxDegree + (j < 0 ? -s : s);
866 final int jIndex = FastMath.abs(j);
867 final HansenTesseralLinear hans = this.hansenObjects[sIndex][jIndex];
868
869
870 for (int n = nmin; n <= maxN; n++) {
871
872 if ((n - s) % 2 == 0) {
873
874
875 final double vMNS = CoefficientsFactory.getVmns(m, n, s);
876
877
878 final double gaMNS = gammaMNS.getValue(m, n, s);
879 final double dGaMNS = gammaMNS.getDerivative(m, n, s);
880
881
882 final double kJNS = hans.getValue(-n - 1, chi);
883 final double dkJNS = hans.getDerivative(-n - 1, chi);
884
885
886 final double gMSJ = ghMSJ.getGmsj(m, s, j);
887 final double hMSJ = ghMSJ.getHmsj(m, s, j);
888 final double dGdh = ghMSJ.getdGmsdh(m, s, j);
889 final double dGdk = ghMSJ.getdGmsdk(m, s, j);
890 final double dGdA = ghMSJ.getdGmsdAlpha(m, s, j);
891 final double dGdB = ghMSJ.getdGmsdBeta(m, s, j);
892 final double dHdh = ghMSJ.getdHmsdh(m, s, j);
893 final double dHdk = ghMSJ.getdHmsdk(m, s, j);
894 final double dHdA = ghMSJ.getdHmsdAlpha(m, s, j);
895 final double dHdB = ghMSJ.getdHmsdBeta(m, s, j);
896
897
898 final int l = FastMath.min(n - m, n - FastMath.abs(s));
899
900 final DerivativeStructure jacobi =
901 JacobiPolynomials.getValue(l, v, w, factory.variable(0, gamma));
902
903
904 final double cnm = harmonics.getUnnormalizedCnm(n, m);
905 final double snm = harmonics.getUnnormalizedSnm(n, m);
906
907
908 final double cf_0 = roaPow[n] * Im * vMNS;
909 final double cf_1 = cf_0 * gaMNS * jacobi.getValue();
910 final double cf_2 = cf_1 * kJNS;
911 final double gcPhs = gMSJ * cnm + hMSJ * snm;
912 final double gsMhc = gMSJ * snm - hMSJ * cnm;
913 final double dKgcPhsx2 = 2. * dkJNS * gcPhs;
914 final double dKgsMhcx2 = 2. * dkJNS * gsMhc;
915 final double dUdaCoef = (n + 1) * cf_2;
916 final double dUdlCoef = j * cf_2;
917 final double dUdGaCoef = cf_0 * kJNS * (jacobi.getValue() * dGaMNS + gaMNS * jacobi.getPartialDerivative(1));
918
919
920 dUdaCos += dUdaCoef * gcPhs;
921 dUdaSin += dUdaCoef * gsMhc;
922
923
924 dUdhCos += cf_1 * (kJNS * (cnm * dGdh + snm * dHdh) + h * dKgcPhsx2);
925 dUdhSin += cf_1 * (kJNS * (snm * dGdh - cnm * dHdh) + h * dKgsMhcx2);
926
927
928 dUdkCos += cf_1 * (kJNS * (cnm * dGdk + snm * dHdk) + k * dKgcPhsx2);
929 dUdkSin += cf_1 * (kJNS * (snm * dGdk - cnm * dHdk) + k * dKgsMhcx2);
930
931
932 dUdlCos += dUdlCoef * gsMhc;
933 dUdlSin += -dUdlCoef * gcPhs;
934
935
936 dUdAlCos += cf_2 * (dGdA * cnm + dHdA * snm);
937 dUdAlSin += cf_2 * (dGdA * snm - dHdA * cnm);
938
939
940 dUdBeCos += cf_2 * (dGdB * cnm + dHdB * snm);
941 dUdBeSin += cf_2 * (dGdB * snm - dHdB * cnm);
942
943
944 dUdGaCos += dUdGaCoef * gcPhs;
945 dUdGaSin += dUdGaCoef * gsMhc;
946 }
947 }
948
949 return new double[][] {{dUdaCos, dUdaSin},
950 {dUdhCos, dUdhSin},
951 {dUdkCos, dUdkSin},
952 {dUdlCos, dUdlSin},
953 {dUdAlCos, dUdAlSin},
954 {dUdBeCos, dUdBeSin},
955 {dUdGaCos, dUdGaSin}};
956 }
957
958
959 @Override
960 public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
961
962 }
963
964
965
966
967
968
969
970 private class FourierCjSjCoefficients {
971
972
973 private final int jMax;
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988 private final double[][][] cCoef;
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003 private final double[][][] sCoef;
1004
1005
1006 private GHmsjPolynomials ghMSJ;
1007
1008
1009 private GammaMnsFunction gammaMNS;
1010
1011
1012 private final double[] roaPow;
1013
1014
1015
1016
1017
1018 FourierCjSjCoefficients(final int jMax, final int mMax) {
1019
1020 final int rows = mMax + 1;
1021 final int columns = 2 * jMax + 1;
1022 this.jMax = jMax;
1023 this.cCoef = new double[rows][columns][6];
1024 this.sCoef = new double[rows][columns][6];
1025 this.roaPow = new double[maxDegree + 1];
1026 roaPow[0] = 1.;
1027 }
1028
1029
1030
1031
1032
1033
1034 public void generateCoefficients(final AbsoluteDate date) throws OrekitException {
1035
1036 if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
1037
1038 ghMSJ = new GHmsjPolynomials(k, h, alpha, beta, I);
1039
1040
1041 gammaMNS = new GammaMnsFunction(maxDegree, gamma, I);
1042
1043 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);
1044
1045
1046 for (int i = 1; i <= maxRoaPower; i++) {
1047 roaPow[i] = roa * roaPow[i - 1];
1048 }
1049
1050
1051 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1052 buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP);
1053 }
1054
1055
1056 if (maxDegreeTesseralSP >= 0) {
1057 for (int m: nonResOrders.keySet()) {
1058 final List<Integer> listJ = nonResOrders.get(m);
1059
1060 for (int j: listJ) {
1061 buildFourierCoefficients(date, m, j, maxDegreeTesseralSP);
1062 }
1063 }
1064 }
1065 }
1066 }
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076 private void buildFourierCoefficients(final AbsoluteDate date,
1077 final int m, final int j, final int maxN) throws OrekitException {
1078
1079 double dRdaCos = 0.;
1080 double dRdaSin = 0.;
1081 double dRdhCos = 0.;
1082 double dRdhSin = 0.;
1083 double dRdkCos = 0.;
1084 double dRdkSin = 0.;
1085 double dRdlCos = 0.;
1086 double dRdlSin = 0.;
1087 double dRdAlCos = 0.;
1088 double dRdAlSin = 0.;
1089 double dRdBeCos = 0.;
1090 double dRdBeSin = 0.;
1091 double dRdGaCos = 0.;
1092 double dRdGaSin = 0.;
1093
1094
1095 final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1096 final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1097 for (int s = 0; s <= sMax; s++) {
1098
1099
1100 final double[][] nSumSpos = computeNSum(date, j, m, s, maxN,
1101 roaPow, ghMSJ, gammaMNS);
1102 dRdaCos += nSumSpos[0][0];
1103 dRdaSin += nSumSpos[0][1];
1104 dRdhCos += nSumSpos[1][0];
1105 dRdhSin += nSumSpos[1][1];
1106 dRdkCos += nSumSpos[2][0];
1107 dRdkSin += nSumSpos[2][1];
1108 dRdlCos += nSumSpos[3][0];
1109 dRdlSin += nSumSpos[3][1];
1110 dRdAlCos += nSumSpos[4][0];
1111 dRdAlSin += nSumSpos[4][1];
1112 dRdBeCos += nSumSpos[5][0];
1113 dRdBeSin += nSumSpos[5][1];
1114 dRdGaCos += nSumSpos[6][0];
1115 dRdGaSin += nSumSpos[6][1];
1116
1117
1118 if (s > 0 && s <= sMin) {
1119 final double[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
1120 roaPow, ghMSJ, gammaMNS);
1121 dRdaCos += nSumSneg[0][0];
1122 dRdaSin += nSumSneg[0][1];
1123 dRdhCos += nSumSneg[1][0];
1124 dRdhSin += nSumSneg[1][1];
1125 dRdkCos += nSumSneg[2][0];
1126 dRdkSin += nSumSneg[2][1];
1127 dRdlCos += nSumSneg[3][0];
1128 dRdlSin += nSumSneg[3][1];
1129 dRdAlCos += nSumSneg[4][0];
1130 dRdAlSin += nSumSneg[4][1];
1131 dRdBeCos += nSumSneg[5][0];
1132 dRdBeSin += nSumSneg[5][1];
1133 dRdGaCos += nSumSneg[6][0];
1134 dRdGaSin += nSumSneg[6][1];
1135 }
1136 }
1137 dRdaCos *= -moa / a;
1138 dRdaSin *= -moa / a;
1139 dRdhCos *= moa;
1140 dRdhSin *= moa;
1141 dRdkCos *= moa;
1142 dRdkSin *= moa;
1143 dRdlCos *= moa;
1144 dRdlSin *= moa;
1145 dRdAlCos *= moa;
1146 dRdAlSin *= moa;
1147 dRdBeCos *= moa;
1148 dRdBeSin *= moa;
1149 dRdGaCos *= moa;
1150 dRdGaSin *= moa;
1151
1152
1153 final double RAlphaGammaCos = alpha * dRdGaCos - gamma * dRdAlCos;
1154 final double RAlphaGammaSin = alpha * dRdGaSin - gamma * dRdAlSin;
1155 final double RAlphaBetaCos = alpha * dRdBeCos - beta * dRdAlCos;
1156 final double RAlphaBetaSin = alpha * dRdBeSin - beta * dRdAlSin;
1157 final double RBetaGammaCos = beta * dRdGaCos - gamma * dRdBeCos;
1158 final double RBetaGammaSin = beta * dRdGaSin - gamma * dRdBeSin;
1159 final double RhkCos = h * dRdkCos - k * dRdhCos;
1160 final double RhkSin = h * dRdkSin - k * dRdhSin;
1161 final double pRagmIqRbgoABCos = (p * RAlphaGammaCos - I * q * RBetaGammaCos) * ooAB;
1162 final double pRagmIqRbgoABSin = (p * RAlphaGammaSin - I * q * RBetaGammaSin) * ooAB;
1163 final double RhkmRabmdRdlCos = RhkCos - RAlphaBetaCos - dRdlCos;
1164 final double RhkmRabmdRdlSin = RhkSin - RAlphaBetaSin - dRdlSin;
1165
1166
1167 cCoef[m][j + jMax][0] = ax2oA * dRdlCos;
1168 sCoef[m][j + jMax][0] = ax2oA * dRdlSin;
1169
1170
1171 cCoef[m][j + jMax][1] = -(BoA * dRdhCos + h * pRagmIqRbgoABCos + k * BoABpo * dRdlCos);
1172 sCoef[m][j + jMax][1] = -(BoA * dRdhSin + h * pRagmIqRbgoABSin + k * BoABpo * dRdlSin);
1173
1174
1175 cCoef[m][j + jMax][2] = BoA * dRdkCos + k * pRagmIqRbgoABCos - h * BoABpo * dRdlCos;
1176 sCoef[m][j + jMax][2] = BoA * dRdkSin + k * pRagmIqRbgoABSin - h * BoABpo * dRdlSin;
1177
1178
1179 cCoef[m][j + jMax][3] = Co2AB * (q * RhkmRabmdRdlCos - I * RAlphaGammaCos);
1180 sCoef[m][j + jMax][3] = Co2AB * (q * RhkmRabmdRdlSin - I * RAlphaGammaSin);
1181
1182
1183 cCoef[m][j + jMax][4] = Co2AB * (p * RhkmRabmdRdlCos - RBetaGammaCos);
1184 sCoef[m][j + jMax][4] = Co2AB * (p * RhkmRabmdRdlSin - RBetaGammaSin);
1185
1186
1187 cCoef[m][j + jMax][5] = -ax2oA * dRdaCos + BoABpo * (h * dRdhCos + k * dRdkCos) + pRagmIqRbgoABCos;
1188 sCoef[m][j + jMax][5] = -ax2oA * dRdaSin + BoABpo * (h * dRdhSin + k * dRdkSin) + pRagmIqRbgoABSin;
1189 }
1190
1191
1192
1193
1194
1195
1196
1197 public double getCijm(final int i, final int j, final int m) {
1198 return cCoef[m][j + jMax][i];
1199 }
1200
1201
1202
1203
1204
1205
1206
1207 public double getSijm(final int i, final int j, final int m) {
1208 return sCoef[m][j + jMax][i];
1209 }
1210 }
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 private static class TesseralShortPeriodicCoefficients implements ShortPeriodTerms {
1222
1223
1224 private static final long serialVersionUID = 20151119L;
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239 private static final int I = 1;
1240
1241
1242 private final Frame bodyFrame;
1243
1244
1245 private final int maxOrderMdailyTesseralSP;
1246
1247
1248 private final boolean mDailiesOnly;
1249
1250
1251 private final SortedMap<Integer, List<Integer> > nonResOrders;
1252
1253
1254 private final int mMax;
1255
1256
1257 private final int jMax;
1258
1259
1260 private final int interpolationPoints;
1261
1262
1263 private final transient TimeSpanMap<Slot> slots;
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275 TesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
1276 final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
1277 final int mMax, final int jMax, final int interpolationPoints,
1278 final TimeSpanMap<Slot> slots) {
1279 this.bodyFrame = bodyFrame;
1280 this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
1281 this.mDailiesOnly = mDailiesOnly;
1282 this.nonResOrders = nonResOrders;
1283 this.mMax = mMax;
1284 this.jMax = jMax;
1285 this.interpolationPoints = interpolationPoints;
1286 this.slots = slots;
1287 }
1288
1289
1290
1291
1292
1293 public Slot createSlot(final SpacecraftState... meanStates) {
1294 final Slot slot = new Slot(mMax, jMax, interpolationPoints);
1295 final AbsoluteDate first = meanStates[0].getDate();
1296 final AbsoluteDate last = meanStates[meanStates.length - 1].getDate();
1297 if (first.compareTo(last) <= 0) {
1298 slots.addValidAfter(slot, first);
1299 } else {
1300 slots.addValidBefore(slot, first);
1301 }
1302 return slot;
1303 }
1304
1305
1306 @Override
1307 public double[] value(final Orbit meanOrbit) throws OrekitException {
1308
1309
1310 final Slot slot = slots.get(meanOrbit.getDate());
1311
1312
1313 final double[] shortPeriodicVariation = new double[6];
1314
1315
1316
1317 if (!nonResOrders.isEmpty() || mDailiesOnly) {
1318
1319
1320 final AuxiliaryElements aux = new AuxiliaryElements(meanOrbit, I);
1321
1322
1323 final Transform t = bodyFrame.getTransformTo(aux.getFrame(), aux.getDate());
1324 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
1325 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
1326 final Vector3D f = aux.getVectorF();
1327 final Vector3D g = aux.getVectorG();
1328 final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
1329 f.dotProduct(xB) + I * g.dotProduct(yB));
1330
1331
1332 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1333
1334 final double jlMmt = -m * currentTheta;
1335 final double sinPhi = FastMath.sin(jlMmt);
1336 final double cosPhi = FastMath.cos(jlMmt);
1337
1338
1339 final double[] c = slot.getCijm(0, m, meanOrbit.getDate());
1340 final double[] s = slot.getSijm(0, m, meanOrbit.getDate());
1341 for (int i = 0; i < 6; i++) {
1342 shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
1343 }
1344 }
1345
1346
1347 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
1348 final int m = entry.getKey();
1349 final List<Integer> listJ = entry.getValue();
1350
1351 for (int j : listJ) {
1352
1353 final double jlMmt = j * meanOrbit.getLM() - m * currentTheta;
1354 final double sinPhi = FastMath.sin(jlMmt);
1355 final double cosPhi = FastMath.cos(jlMmt);
1356
1357
1358 final double[] c = slot.getCijm(j, m, meanOrbit.getDate());
1359 final double[] s = slot.getSijm(j, m, meanOrbit.getDate());
1360 for (int i = 0; i < 6; i++) {
1361 shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
1362 }
1363
1364 }
1365 }
1366 }
1367
1368 return shortPeriodicVariation;
1369
1370 }
1371
1372
1373 @Override
1374 public String getCoefficientsKeyPrefix() {
1375 return "DSST-central-body-tesseral-";
1376 }
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388 @Override
1389 public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected)
1390 throws OrekitException {
1391
1392
1393 final Slot slot = slots.get(date);
1394
1395 if (!nonResOrders.isEmpty() || mDailiesOnly) {
1396 final Map<String, double[]> coefficients =
1397 new HashMap<String, double[]>(12 * maxOrderMdailyTesseralSP +
1398 12 * nonResOrders.size());
1399
1400 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1401 storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), "cM", m);
1402 storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), "sM", m);
1403 }
1404
1405 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
1406 final int m = entry.getKey();
1407 final List<Integer> listJ = entry.getValue();
1408
1409 for (int j : listJ) {
1410 for (int i = 0; i < 6; ++i) {
1411 storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
1412 storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
1413 }
1414 }
1415 }
1416
1417 return coefficients;
1418
1419 } else {
1420 return Collections.emptyMap();
1421 }
1422
1423 }
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433 private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
1434 final double[] value, final String id, final int... indices) {
1435 final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
1436 keyBuilder.append(id);
1437 for (int index : indices) {
1438 keyBuilder.append('[').append(index).append(']');
1439 }
1440 final String key = keyBuilder.toString();
1441 if (selected.isEmpty() || selected.contains(key)) {
1442 map.put(key, value);
1443 }
1444 }
1445
1446
1447
1448
1449
1450 private Object writeReplace() throws NotSerializableException {
1451
1452
1453 final SortedSet<TimeSpanMap.Transition<Slot>> transitions = slots.getTransitions();
1454 final AbsoluteDate[] transitionDates = new AbsoluteDate[transitions.size()];
1455 final Slot[] allSlots = new Slot[transitions.size() + 1];
1456 int i = 0;
1457 for (final TimeSpanMap.Transition<Slot> transition : transitions) {
1458 if (i == 0) {
1459
1460 allSlots[i] = transition.getBefore();
1461 }
1462 if (i < transitionDates.length) {
1463 transitionDates[i] = transition.getDate();
1464 allSlots[++i] = transition.getAfter();
1465 }
1466 }
1467
1468 return new DataTransferObject(bodyFrame, maxOrderMdailyTesseralSP,
1469 mDailiesOnly, nonResOrders,
1470 mMax, jMax, interpolationPoints,
1471 transitionDates, allSlots);
1472
1473 }
1474
1475
1476
1477 private static class DataTransferObject implements Serializable {
1478
1479
1480 private static final long serialVersionUID = 20160319L;
1481
1482
1483 private final Frame bodyFrame;
1484
1485
1486 private final int maxOrderMdailyTesseralSP;
1487
1488
1489 private final boolean mDailiesOnly;
1490
1491
1492 private final SortedMap<Integer, List<Integer> > nonResOrders;
1493
1494
1495 private final int mMax;
1496
1497
1498 private final int jMax;
1499
1500
1501 private final int interpolationPoints;
1502
1503
1504 private final AbsoluteDate[] transitionDates;
1505
1506
1507 private final Slot[] allSlots;
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520 DataTransferObject(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
1521 final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
1522 final int mMax, final int jMax, final int interpolationPoints,
1523 final AbsoluteDate[] transitionDates, final Slot[] allSlots) {
1524 this.bodyFrame = bodyFrame;
1525 this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
1526 this.mDailiesOnly = mDailiesOnly;
1527 this.nonResOrders = nonResOrders;
1528 this.mMax = mMax;
1529 this.jMax = jMax;
1530 this.interpolationPoints = interpolationPoints;
1531 this.transitionDates = transitionDates;
1532 this.allSlots = allSlots;
1533 }
1534
1535
1536
1537
1538 private Object readResolve() {
1539
1540 final TimeSpanMap<Slot> slots = new TimeSpanMap<Slot>(allSlots[0]);
1541 for (int i = 0; i < transitionDates.length; ++i) {
1542 slots.addValidAfter(allSlots[i + 1], transitionDates[i]);
1543 }
1544
1545 return new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP, mDailiesOnly,
1546 nonResOrders, mMax, jMax, interpolationPoints,
1547 slots);
1548
1549 }
1550
1551 }
1552
1553 }
1554
1555
1556 private static class Slot implements Serializable {
1557
1558
1559 private static final long serialVersionUID = 20160319L;
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573 private final ShortPeriodicsInterpolatedCoefficient[][] cijm;
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587 private final ShortPeriodicsInterpolatedCoefficient[][] sijm;
1588
1589
1590
1591
1592
1593
1594 Slot(final int mMax, final int jMax, final int interpolationPoints) {
1595
1596 final int rows = mMax + 1;
1597 final int columns = 2 * jMax + 1;
1598 cijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
1599 sijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
1600 for (int m = 1; m <= mMax; m++) {
1601 for (int j = -jMax; j <= jMax; j++) {
1602 cijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
1603 sijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
1604 }
1605 }
1606
1607 }
1608
1609
1610
1611
1612
1613
1614
1615
1616 double[] getCijm(final int j, final int m, final AbsoluteDate date) {
1617 final int jMax = (cijm[m].length - 1) / 2;
1618 return cijm[m][j + jMax].value(date);
1619 }
1620
1621
1622
1623
1624
1625
1626
1627
1628 double[] getSijm(final int j, final int m, final AbsoluteDate date) {
1629 final int jMax = (cijm[m].length - 1) / 2;
1630 return sijm[m][j + jMax].value(date);
1631 }
1632
1633 }
1634
1635 }