1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.forces.gravity;
18
19
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.hipparchus.CalculusFieldElement;
24 import org.hipparchus.analysis.differentiation.Gradient;
25 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26 import org.hipparchus.geometry.euclidean.threed.SphericalCoordinates;
27 import org.hipparchus.geometry.euclidean.threed.Vector3D;
28 import org.hipparchus.linear.Array2DRowRealMatrix;
29 import org.hipparchus.linear.RealMatrix;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.MathArrays;
32 import org.orekit.forces.ForceModel;
33 import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
34 import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider.NormalizedSphericalHarmonics;
35 import org.orekit.forces.gravity.potential.TideSystem;
36 import org.orekit.forces.gravity.potential.TideSystemProvider;
37 import org.orekit.frames.FieldStaticTransform;
38 import org.orekit.frames.Frame;
39 import org.orekit.frames.StaticTransform;
40 import org.orekit.propagation.FieldSpacecraftState;
41 import org.orekit.propagation.SpacecraftState;
42 import org.orekit.time.AbsoluteDate;
43 import org.orekit.time.FieldAbsoluteDate;
44 import org.orekit.time.TimeInterval;
45 import org.orekit.utils.FieldPVCoordinates;
46 import org.orekit.utils.drivers.ParameterDriver;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class HolmesFeatherstoneAttractionModel implements ForceModel, TideSystemProvider {
76
77
78
79
80
81 private static final int SCALING = 930;
82
83
84
85
86
87
88
89 private static final double MU_SCALE = FastMath.scalb(1.0, 32);
90
91
92 private final ParameterDriver gmParameterDriver;
93
94
95 private final NormalizedSphericalHarmonicsProvider provider;
96
97
98 private final Frame bodyFrame;
99
100
101 private final double[] gnmOj;
102
103
104 private final double[] hnmOj;
105
106
107 private final double[] enm;
108
109
110 private final double[] sectorial;
111
112
113
114
115
116
117 public HolmesFeatherstoneAttractionModel(final Frame centralBodyFrame,
118 final NormalizedSphericalHarmonicsProvider provider) {
119
120 gmParameterDriver = new ParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
121 provider.getMu(), MU_SCALE,
122 0.0, Double.POSITIVE_INFINITY, TimeInterval.UNLIMITED);
123
124 this.provider = provider;
125 this.bodyFrame = centralBodyFrame;
126
127
128
129 final int degree = provider.getMaxDegree();
130 final int size = FastMath.max(0, degree * (degree + 1) / 2 - 1);
131 gnmOj = new double[size];
132 hnmOj = new double[size];
133 enm = new double[size];
134
135
136
137
138
139 int index = 0;
140 for (int m = degree; m >= 0; --m) {
141 final int j = (m == 0) ? 2 : 1;
142 for (int n = FastMath.max(2, m + 1); n <= degree; ++n) {
143 final double f = ((double) (n - m)) * (n + m + 1);
144 gnmOj[index] = 2 * (m + 1) / FastMath.sqrt(j * f);
145 hnmOj[index] = FastMath.sqrt((n + m + 2) * (n - m - 1) / (j * f));
146 enm[index] = FastMath.sqrt(f / j);
147 ++index;
148 }
149 }
150
151
152 sectorial = new double[degree + 1];
153 sectorial[0] = FastMath.scalb(1.0, -SCALING);
154 if (degree > 0) {
155 sectorial[1] = FastMath.sqrt(3) * sectorial[0];
156 }
157 for (int m = 2; m < sectorial.length; ++m) {
158 sectorial[m] = FastMath.sqrt((2 * m + 1) / (2.0 * m)) * sectorial[m - 1];
159 }
160
161 }
162
163
164 @Override
165 public boolean dependsOnPositionOnly() {
166 return true;
167 }
168
169
170 public TideSystem getTideSystem() {
171 return provider.getTideSystem();
172 }
173
174
175
176
177
178
179
180 public double getMu() {
181 return gmParameterDriver.getValue();
182 }
183
184
185
186
187
188 public double getMu(final AbsoluteDate date) {
189 return gmParameterDriver.getValue();
190 }
191
192
193
194
195
196
197
198 public double value(final AbsoluteDate date, final Vector3D position,
199 final double mu) {
200 return mu / position.getNorm() + nonCentralPart(date, position, mu);
201 }
202
203
204
205
206
207
208
209 public double nonCentralPart(final AbsoluteDate date, final Vector3D position, final double mu) {
210
211 final int degree = provider.getMaxDegree();
212 final int order = provider.getMaxOrder();
213 final NormalizedSphericalHarmonics harmonics = provider.onDate(date);
214
215
216 double[] pnm0Plus2 = new double[degree + 1];
217 double[] pnm0Plus1 = new double[degree + 1];
218 double[] pnm0 = new double[degree + 1];
219
220
221 final double x = position.getX();
222 final double y = position.getY();
223 final double z = position.getZ();
224 final double x2 = x * x;
225 final double y2 = y * y;
226 final double z2 = z * z;
227 final double rho2 = x2 + y2;
228 final double r2 = rho2 + z2;
229 final double r = FastMath.sqrt(r2);
230 final double rho = FastMath.sqrt(rho2);
231 final double t = z / r;
232 final double u = rho / r;
233 final double tOu = z / rho;
234
235
236 final double[] aOrN = createDistancePowersArray(provider.getAe() / r);
237
238
239 final double[][] cosSinLambda = createCosSinArrays(x / rho, y / rho);
240
241
242 int index = 0;
243 double value = 0;
244 for (int m = degree; m >= 0; --m) {
245
246
247 index = computeTesseral(m, degree, index, t, u, tOu,
248 pnm0Plus2, pnm0Plus1, null, pnm0, null, null);
249
250 if (m <= order) {
251
252
253
254 double sumDegreeS = 0;
255 double sumDegreeC = 0;
256 for (int n = FastMath.max(2, m); n <= degree; ++n) {
257 sumDegreeS += pnm0[n] * aOrN[n] * harmonics.getNormalizedSnm(n, m);
258 sumDegreeC += pnm0[n] * aOrN[n] * harmonics.getNormalizedCnm(n, m);
259 }
260
261
262 value = value * u + cosSinLambda[1][m] * sumDegreeS + cosSinLambda[0][m] * sumDegreeC;
263
264 }
265
266
267 final double[] tmp = pnm0Plus2;
268 pnm0Plus2 = pnm0Plus1;
269 pnm0Plus1 = pnm0;
270 pnm0 = tmp;
271
272 }
273
274
275 value = FastMath.scalb(value, SCALING);
276
277
278 return mu * value / r;
279
280 }
281
282
283
284
285
286
287
288
289
290
291
292 public double[] gradient(final AbsoluteDate date, final Vector3D position, final double mu) {
293
294 final int degree = provider.getMaxDegree();
295 final int order = provider.getMaxOrder();
296 final NormalizedSphericalHarmonics harmonics = provider.onDate(date);
297
298
299 double[] pnm0Plus2 = new double[degree + 1];
300 double[] pnm0Plus1 = new double[degree + 1];
301 double[] pnm0 = new double[degree + 1];
302 final double[] pnm1 = new double[degree + 1];
303
304
305 final double x = position.getX();
306 final double y = position.getY();
307 final double z = position.getZ();
308 final double x2 = x * x;
309 final double y2 = y * y;
310 final double z2 = z * z;
311 final double r2 = x2 + y2 + z2;
312 final double r = FastMath.sqrt (r2);
313 final double rho2 = x2 + y2;
314 final double rho = FastMath.sqrt(rho2);
315 final double t = z / r;
316 final double u = rho / r;
317 final double tOu = z / rho;
318
319
320 final double[] aOrN = createDistancePowersArray(provider.getAe() / r);
321
322
323 final double[][] cosSinLambda = createCosSinArrays(x / rho, y / rho);
324
325
326 int index = 0;
327 double value = 0;
328 final double[] gradient = new double[3];
329 for (int m = degree; m >= 0; --m) {
330
331
332 index = computeTesseral(m, degree, index, t, u, tOu,
333 pnm0Plus2, pnm0Plus1, null, pnm0, pnm1, null);
334
335 if (m <= order) {
336
337
338
339 double sumDegreeS = 0;
340 double sumDegreeC = 0;
341 double dSumDegreeSdR = 0;
342 double dSumDegreeCdR = 0;
343 double dSumDegreeSdTheta = 0;
344 double dSumDegreeCdTheta = 0;
345 for (int n = FastMath.max(2, m); n <= degree; ++n) {
346 final double qSnm = aOrN[n] * harmonics.getNormalizedSnm(n, m);
347 final double qCnm = aOrN[n] * harmonics.getNormalizedCnm(n, m);
348 final double nOr = n / r;
349 final double s0 = pnm0[n] * qSnm;
350 final double c0 = pnm0[n] * qCnm;
351 final double s1 = pnm1[n] * qSnm;
352 final double c1 = pnm1[n] * qCnm;
353 sumDegreeS += s0;
354 sumDegreeC += c0;
355 dSumDegreeSdR -= nOr * s0;
356 dSumDegreeCdR -= nOr * c0;
357 dSumDegreeSdTheta += s1;
358 dSumDegreeCdTheta += c1;
359 }
360
361
362
363
364
365 final double sML = cosSinLambda[1][m];
366 final double cML = cosSinLambda[0][m];
367 value = value * u + sML * sumDegreeS + cML * sumDegreeC;
368 gradient[0] = gradient[0] * u + sML * dSumDegreeSdR + cML * dSumDegreeCdR;
369 gradient[1] = gradient[1] * u + m * (cML * sumDegreeS - sML * sumDegreeC);
370 gradient[2] = gradient[2] * u + sML * dSumDegreeSdTheta + cML * dSumDegreeCdTheta;
371
372 }
373
374
375 final double[] tmp = pnm0Plus2;
376 pnm0Plus2 = pnm0Plus1;
377 pnm0Plus1 = pnm0;
378 pnm0 = tmp;
379
380 }
381
382
383 value = FastMath.scalb(value, SCALING);
384 gradient[0] = FastMath.scalb(gradient[0], SCALING);
385 gradient[1] = FastMath.scalb(gradient[1], SCALING);
386 gradient[2] = FastMath.scalb(gradient[2], SCALING);
387
388
389 final double muOr = mu / r;
390 value *= muOr;
391 gradient[0] = muOr * gradient[0] - value / r;
392 gradient[1] *= muOr;
393 gradient[2] *= muOr;
394
395
396 return new SphericalCoordinates(position).toCartesianGradient(gradient);
397
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411 public <T extends CalculusFieldElement<T>> T[] gradient(final FieldAbsoluteDate<T> date, final FieldVector3D<T> position,
412 final T mu) {
413
414 final int degree = provider.getMaxDegree();
415 final int order = provider.getMaxOrder();
416 final NormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());
417 final T zero = date.getField().getZero();
418
419 T[] pnm0Plus2 = MathArrays.buildArray(date.getField(), degree + 1);
420 T[] pnm0Plus1 = MathArrays.buildArray(date.getField(), degree + 1);
421 T[] pnm0 = MathArrays.buildArray(date.getField(), degree + 1);
422 final T[] pnm1 = MathArrays.buildArray(date.getField(), degree + 1);
423
424
425 final T x = position.getX();
426 final T y = position.getY();
427 final T z = position.getZ();
428 final T x2 = x.square();
429 final T y2 = y.square();
430 final T rho2 = x2.add(y2);
431 final T rho = rho2.sqrt();
432 final T z2 = z.square();
433 final T r2 = rho2.add(z2);
434 final T r = r2.sqrt();
435 final T t = z.divide(r);
436 final T u = rho.divide(r);
437 final T tOu = z.divide(rho);
438
439
440 final T[] aOrN = createDistancePowersArray(r.reciprocal().multiply(provider.getAe()));
441
442
443 final T[][] cosSinLambda = createCosSinArrays(x.divide(rho), y.divide(rho));
444
445 int index = 0;
446 T value = zero;
447 final T[] gradient = MathArrays.buildArray(zero.getField(), 3);
448 for (int m = degree; m >= 0; --m) {
449
450
451 index = computeTesseral(m, degree, index, t, u, tOu,
452 pnm0Plus2, pnm0Plus1, null, pnm0, pnm1, null);
453 if (m <= order) {
454
455
456
457 T sumDegreeS = zero;
458 T sumDegreeC = zero;
459 T dSumDegreeSdR = zero;
460 T dSumDegreeCdR = zero;
461 T dSumDegreeSdTheta = zero;
462 T dSumDegreeCdTheta = zero;
463 for (int n = FastMath.max(2, m); n <= degree; ++n) {
464 final T qSnm = aOrN[n].multiply(harmonics.getNormalizedSnm(n, m));
465 final T qCnm = aOrN[n].multiply(harmonics.getNormalizedCnm(n, m));
466 final T nOr = r.reciprocal().multiply(n);
467 final T s0 = pnm0[n].multiply(qSnm);
468 final T c0 = pnm0[n].multiply(qCnm);
469 final T s1 = pnm1[n].multiply(qSnm);
470 final T c1 = pnm1[n].multiply(qCnm);
471 sumDegreeS = sumDegreeS .add(s0);
472 sumDegreeC = sumDegreeC .add(c0);
473 dSumDegreeSdR = dSumDegreeSdR .subtract(nOr.multiply(s0));
474 dSumDegreeCdR = dSumDegreeCdR .subtract(nOr.multiply(c0));
475 dSumDegreeSdTheta = dSumDegreeSdTheta.add(s1);
476 dSumDegreeCdTheta = dSumDegreeCdTheta.add(c1);
477 }
478
479
480
481
482
483 final T sML = cosSinLambda[1][m];
484 final T cML = cosSinLambda[0][m];
485 value = value .multiply(u).add(sML.multiply(sumDegreeS )).add(cML.multiply(sumDegreeC));
486 gradient[0] = gradient[0].multiply(u).add(sML.multiply(dSumDegreeSdR)).add(cML.multiply(dSumDegreeCdR));
487 gradient[1] = gradient[1].multiply(u).add(cML.multiply(sumDegreeS).subtract(sML.multiply(sumDegreeC)).multiply(m));
488 gradient[2] = gradient[2].multiply(u).add(sML.multiply(dSumDegreeSdTheta)).add(cML.multiply(dSumDegreeCdTheta));
489 }
490
491 final T[] tmp = pnm0Plus2;
492 pnm0Plus2 = pnm0Plus1;
493 pnm0Plus1 = pnm0;
494 pnm0 = tmp;
495
496 }
497
498 value = value.scalb(SCALING);
499 gradient[0] = gradient[0].scalb(SCALING);
500 gradient[1] = gradient[1].scalb(SCALING);
501 gradient[2] = gradient[2].scalb(SCALING);
502
503
504 final T muOr = r.reciprocal().multiply(mu);
505 value = value.multiply(muOr);
506 gradient[0] = muOr.multiply(gradient[0]).subtract(value.divide(r));
507 gradient[1] = gradient[1].multiply(muOr);
508 gradient[2] = gradient[2].multiply(muOr);
509
510
511
512
513
514
515 final T xPos = position.getX();
516 final T yPos = position.getY();
517 final T zPos = position.getZ();
518 final T rho2Pos = x.square().add(y.square());
519 final T rhoPos = rho2.sqrt();
520 final T r2Pos = rho2.add(z.square());
521 final T rPos = r2Pos.sqrt();
522
523 final T[][] jacobianPos = MathArrays.buildArray(zero.getField(), 3, 3);
524
525
526 jacobianPos[0][0] = xPos.divide(rPos);
527 jacobianPos[0][1] = yPos.divide(rPos);
528 jacobianPos[0][2] = zPos.divide(rPos);
529
530
531 jacobianPos[1][0] = yPos.negate().divide(rho2Pos);
532 jacobianPos[1][1] = xPos.divide(rho2Pos);
533
534
535
536 final T rhoPosTimesR2Pos = rhoPos.multiply(r2Pos);
537 jacobianPos[2][0] = xPos.multiply(zPos).divide(rhoPosTimesR2Pos);
538 jacobianPos[2][1] = yPos.multiply(zPos).divide(rhoPosTimesR2Pos);
539 jacobianPos[2][2] = rhoPos.negate().divide(r2Pos);
540 final T[] cartGradPos = MathArrays.buildArray(zero.getField(), 3);
541 cartGradPos[0] = gradient[0].multiply(jacobianPos[0][0]).add(gradient[1].multiply(jacobianPos[1][0])).add(gradient[2].multiply(jacobianPos[2][0]));
542 cartGradPos[1] = gradient[0].multiply(jacobianPos[0][1]).add(gradient[1].multiply(jacobianPos[1][1])).add(gradient[2].multiply(jacobianPos[2][1]));
543 cartGradPos[2] = gradient[0].multiply(jacobianPos[0][2]) .add(gradient[2].multiply(jacobianPos[2][2]));
544 return cartGradPos;
545
546 }
547
548
549
550
551
552
553
554
555
556
557
558 private GradientHessian gradientHessian(final AbsoluteDate date, final Vector3D position, final double mu) {
559
560 final int degree = provider.getMaxDegree();
561 final int order = provider.getMaxOrder();
562 final NormalizedSphericalHarmonics harmonics = provider.onDate(date);
563
564
565 double[] pnm0Plus2 = new double[degree + 1];
566 double[] pnm0Plus1 = new double[degree + 1];
567 double[] pnm0 = new double[degree + 1];
568 double[] pnm1Plus1 = new double[degree + 1];
569 double[] pnm1 = new double[degree + 1];
570 final double[] pnm2 = new double[degree + 1];
571
572
573 final double x = position.getX();
574 final double y = position.getY();
575 final double z = position.getZ();
576 final double x2 = x * x;
577 final double y2 = y * y;
578 final double z2 = z * z;
579 final double rho2 = x2 + y2;
580 final double rho = FastMath.sqrt(rho2);
581 final double r2 = rho2 + z2;
582 final double r = FastMath.sqrt(r2);
583 final double t = z / r;
584 final double u = rho / r;
585 final double tOu = z / rho;
586
587
588 final double[] aOrN = createDistancePowersArray(provider.getAe() / r);
589
590
591 final double[][] cosSinLambda = createCosSinArrays(x / rho, y / rho);
592
593
594 int index = 0;
595 double value = 0;
596 final double[] gradient = new double[3];
597 final double[][] hessian = new double[3][3];
598 for (int m = degree; m >= 0; --m) {
599
600
601 index = computeTesseral(m, degree, index, t, u, tOu,
602 pnm0Plus2, pnm0Plus1, pnm1Plus1, pnm0, pnm1, pnm2);
603
604 if (m <= order) {
605
606
607
608 double sumDegreeS = 0;
609 double sumDegreeC = 0;
610 double dSumDegreeSdR = 0;
611 double dSumDegreeCdR = 0;
612 double dSumDegreeSdTheta = 0;
613 double dSumDegreeCdTheta = 0;
614 double d2SumDegreeSdRdR = 0;
615 double d2SumDegreeSdRdTheta = 0;
616 double d2SumDegreeSdThetadTheta = 0;
617 double d2SumDegreeCdRdR = 0;
618 double d2SumDegreeCdRdTheta = 0;
619 double d2SumDegreeCdThetadTheta = 0;
620 for (int n = FastMath.max(2, m); n <= degree; ++n) {
621 final double qSnm = aOrN[n] * harmonics.getNormalizedSnm(n, m);
622 final double qCnm = aOrN[n] * harmonics.getNormalizedCnm(n, m);
623 final double nOr = n / r;
624 final double nnP1Or2 = nOr * (n + 1) / r;
625 final double s0 = pnm0[n] * qSnm;
626 final double c0 = pnm0[n] * qCnm;
627 final double s1 = pnm1[n] * qSnm;
628 final double c1 = pnm1[n] * qCnm;
629 final double s2 = pnm2[n] * qSnm;
630 final double c2 = pnm2[n] * qCnm;
631 sumDegreeS += s0;
632 sumDegreeC += c0;
633 dSumDegreeSdR -= nOr * s0;
634 dSumDegreeCdR -= nOr * c0;
635 dSumDegreeSdTheta += s1;
636 dSumDegreeCdTheta += c1;
637 d2SumDegreeSdRdR += nnP1Or2 * s0;
638 d2SumDegreeSdRdTheta -= nOr * s1;
639 d2SumDegreeSdThetadTheta += s2;
640 d2SumDegreeCdRdR += nnP1Or2 * c0;
641 d2SumDegreeCdRdTheta -= nOr * c1;
642 d2SumDegreeCdThetadTheta += c2;
643 }
644
645
646 final double sML = cosSinLambda[1][m];
647 final double cML = cosSinLambda[0][m];
648 value = value * u + sML * sumDegreeS + cML * sumDegreeC;
649 gradient[0] = gradient[0] * u + sML * dSumDegreeSdR + cML * dSumDegreeCdR;
650 gradient[1] = gradient[1] * u + m * (cML * sumDegreeS - sML * sumDegreeC);
651 gradient[2] = gradient[2] * u + sML * dSumDegreeSdTheta + cML * dSumDegreeCdTheta;
652 hessian[0][0] = hessian[0][0] * u + sML * d2SumDegreeSdRdR + cML * d2SumDegreeCdRdR;
653 hessian[1][0] = hessian[1][0] * u + m * (cML * dSumDegreeSdR - sML * dSumDegreeCdR);
654 hessian[2][0] = hessian[2][0] * u + sML * d2SumDegreeSdRdTheta + cML * d2SumDegreeCdRdTheta;
655 hessian[1][1] = hessian[1][1] * u - m * m * (sML * sumDegreeS + cML * sumDegreeC);
656 hessian[2][1] = hessian[2][1] * u + m * (cML * dSumDegreeSdTheta - sML * dSumDegreeCdTheta);
657 hessian[2][2] = hessian[2][2] * u + sML * d2SumDegreeSdThetadTheta + cML * d2SumDegreeCdThetadTheta;
658
659 }
660
661
662 final double[] tmp0 = pnm0Plus2;
663 pnm0Plus2 = pnm0Plus1;
664 pnm0Plus1 = pnm0;
665 pnm0 = tmp0;
666 final double[] tmp1 = pnm1Plus1;
667 pnm1Plus1 = pnm1;
668 pnm1 = tmp1;
669
670 }
671
672
673 value = FastMath.scalb(value, SCALING);
674 for (int i = 0; i < 3; ++i) {
675 gradient[i] = FastMath.scalb(gradient[i], SCALING);
676 for (int j = 0; j <= i; ++j) {
677 hessian[i][j] = FastMath.scalb(hessian[i][j], SCALING);
678 }
679 }
680
681
682
683 final double muOr = mu / r;
684 value *= muOr;
685 gradient[0] = muOr * gradient[0] - value / r;
686 gradient[1] *= muOr;
687 gradient[2] *= muOr;
688 hessian[0][0] = muOr * hessian[0][0] - 2 * gradient[0] / r;
689 hessian[1][0] = muOr * hessian[1][0] - gradient[1] / r;
690 hessian[2][0] = muOr * hessian[2][0] - gradient[2] / r;
691 hessian[1][1] *= muOr;
692 hessian[2][1] *= muOr;
693 hessian[2][2] *= muOr;
694
695
696 final SphericalCoordinates sc = new SphericalCoordinates(position);
697 return new GradientHessian(sc.toCartesianGradient(gradient),
698 sc.toCartesianHessian(hessian, gradient));
699
700
701 }
702
703
704 private static class GradientHessian {
705
706
707 private final double[] gradient;
708
709
710 private final double[][] hessian;
711
712
713
714
715
716
717
718
719 GradientHessian(final double[] gradient, final double[][] hessian) {
720 this.gradient = gradient;
721 this.hessian = hessian;
722 }
723
724
725
726
727 public double[] getGradient() {
728 return gradient;
729 }
730
731
732
733
734 public double[][] getHessian() {
735 return hessian;
736 }
737
738 }
739
740
741
742
743
744 private double[] createDistancePowersArray(final double aOr) {
745
746
747 final double[] aOrN = new double[provider.getMaxDegree() + 1];
748 aOrN[0] = 1;
749 if (provider.getMaxDegree() > 0) {
750 aOrN[1] = aOr;
751 }
752
753
754 for (int n = 2; n < aOrN.length; ++n) {
755 final int p = n / 2;
756 final int q = n - p;
757 aOrN[n] = aOrN[p] * aOrN[q];
758 }
759
760 return aOrN;
761
762 }
763
764
765
766
767
768 private <T extends CalculusFieldElement<T>> T[] createDistancePowersArray(final T aOr) {
769
770
771 final T[] aOrN = MathArrays.buildArray(aOr.getField(), provider.getMaxDegree() + 1);
772 aOrN[0] = aOr.getField().getOne();
773 if (provider.getMaxDegree() > 0) {
774 aOrN[1] = aOr;
775 }
776
777
778 for (int n = 2; n < aOrN.length; ++n) {
779 final int p = n / 2;
780 final int q = n - p;
781 aOrN[n] = aOrN[p].multiply(aOrN[q]);
782 }
783
784 return aOrN;
785
786 }
787
788
789
790
791
792
793
794 private double[][] createCosSinArrays(final double cosLambda, final double sinLambda) {
795
796
797 final double[][] cosSin = new double[2][provider.getMaxOrder() + 1];
798 cosSin[0][0] = 1;
799 cosSin[1][0] = 0;
800 if (provider.getMaxOrder() > 0) {
801 cosSin[0][1] = cosLambda;
802 cosSin[1][1] = sinLambda;
803
804
805 for (int m = 2; m < cosSin[0].length; ++m) {
806
807
808
809
810
811
812 final int p = m / 2;
813 final int q = m - p;
814
815 cosSin[0][m] = cosSin[0][p] * cosSin[0][q] - cosSin[1][p] * cosSin[1][q];
816 cosSin[1][m] = cosSin[1][p] * cosSin[0][q] + cosSin[0][p] * cosSin[1][q];
817 }
818 }
819
820 return cosSin;
821
822 }
823
824
825
826
827
828
829
830
831 private <T extends CalculusFieldElement<T>> T[][] createCosSinArrays(final T cosLambda, final T sinLambda) {
832
833 final T one = cosLambda.getField().getOne();
834 final T zero = cosLambda.getField().getZero();
835
836 final T[][] cosSin = MathArrays.buildArray(one.getField(), 2, provider.getMaxOrder() + 1);
837 cosSin[0][0] = one;
838 cosSin[1][0] = zero;
839 if (provider.getMaxOrder() > 0) {
840 cosSin[0][1] = cosLambda;
841 cosSin[1][1] = sinLambda;
842
843
844 for (int m = 2; m < cosSin[0].length; ++m) {
845
846
847
848
849
850
851 final int p = m / 2;
852 final int q = m - p;
853
854 cosSin[0][m] = cosSin[0][p].multiply(cosSin[0][q]).subtract(cosSin[1][p].multiply(cosSin[1][q]));
855 cosSin[1][m] = cosSin[1][p].multiply(cosSin[0][q]).add(cosSin[0][p].multiply(cosSin[1][q]));
856
857 }
858 }
859
860 return cosSin;
861
862 }
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885 private int computeTesseral(final int m, final int degree, final int index,
886 final double t, final double u, final double tOu,
887 final double[] pnm0Plus2, final double[] pnm0Plus1, final double[] pnm1Plus1,
888 final double[] pnm0, final double[] pnm1, final double[] pnm2) {
889
890 final double u2 = u * u;
891
892
893 int n = FastMath.max(2, m);
894 if (n == m) {
895 pnm0[n] = sectorial[n];
896 ++n;
897 }
898
899
900 int localIndex = index;
901 while (n <= degree) {
902
903
904 pnm0[n] = gnmOj[localIndex] * t * pnm0Plus1[n] - hnmOj[localIndex] * u2 * pnm0Plus2[n];
905
906 ++localIndex;
907 ++n;
908
909 }
910
911 if (pnm1 != null) {
912
913
914 n = FastMath.max(2, m);
915 if (n == m) {
916 pnm1[n] = m * tOu * pnm0[n];
917 ++n;
918 }
919
920
921 localIndex = index;
922 while (n <= degree) {
923
924
925 pnm1[n] = m * tOu * pnm0[n] - enm[localIndex] * u * pnm0Plus1[n];
926
927 ++localIndex;
928 ++n;
929
930 }
931
932 if (pnm2 != null) {
933
934
935 n = FastMath.max(2, m);
936 if (n == m) {
937 pnm2[n] = m * (tOu * pnm1[n] - pnm0[n] / u2);
938 ++n;
939 }
940
941
942 localIndex = index;
943 while (n <= degree) {
944
945
946 pnm2[n] = m * (tOu * pnm1[n] - pnm0[n] / u2) - enm[localIndex] * u * pnm1Plus1[n];
947
948 ++localIndex;
949 ++n;
950
951 }
952
953 }
954
955 }
956
957 return localIndex;
958
959 }
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983 private <T extends CalculusFieldElement<T>> int computeTesseral(final int m, final int degree, final int index,
984 final T t, final T u, final T tOu,
985 final T[] pnm0Plus2, final T[] pnm0Plus1, final T[] pnm1Plus1,
986 final T[] pnm0, final T[] pnm1, final T[] pnm2) {
987
988 final T u2 = u.square();
989 final T zero = u.getField().getZero();
990
991 int n = FastMath.max(2, m);
992 if (n == m) {
993 pnm0[n] = zero.newInstance(sectorial[n]);
994 ++n;
995 }
996
997
998 int localIndex = index;
999 while (n <= degree) {
1000
1001
1002 pnm0[n] = t.multiply(gnmOj[localIndex]).multiply(pnm0Plus1[n]).subtract(u2.multiply(pnm0Plus2[n]).multiply(hnmOj[localIndex]));
1003 ++localIndex;
1004 ++n;
1005
1006 }
1007 if (pnm1 != null) {
1008
1009
1010 n = FastMath.max(2, m);
1011 if (n == m) {
1012 pnm1[n] = tOu.multiply(m).multiply(pnm0[n]);
1013 ++n;
1014 }
1015
1016
1017 localIndex = index;
1018 while (n <= degree) {
1019
1020
1021 pnm1[n] = tOu.multiply(m).multiply(pnm0[n]).subtract(u.multiply(enm[localIndex]).multiply(pnm0Plus1[n]));
1022
1023 ++localIndex;
1024 ++n;
1025
1026 }
1027
1028 if (pnm2 != null) {
1029
1030
1031 n = FastMath.max(2, m);
1032 if (n == m) {
1033 pnm2[n] = tOu.multiply(pnm1[n]).subtract(pnm0[n].divide(u2)).multiply(m);
1034 ++n;
1035 }
1036
1037
1038 localIndex = index;
1039 while (n <= degree) {
1040
1041
1042 pnm2[n] = tOu.multiply(pnm1[n]).subtract(pnm0[n].divide(u2)).multiply(m).subtract(u.multiply(pnm1Plus1[n]).multiply(enm[localIndex]));
1043 ++localIndex;
1044 ++n;
1045
1046 }
1047
1048 }
1049
1050 }
1051 return localIndex;
1052
1053 }
1054
1055
1056 @Override
1057 public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
1058
1059 final double mu = parameters[0];
1060
1061
1062 final AbsoluteDate date = s.getDate();
1063 final StaticTransform fromBodyFrame = bodyFrame.getStaticTransformTo(s.getFrame(), date);
1064 final StaticTransform toBodyFrame = fromBodyFrame.getInverse();
1065 final Vector3D position = toBodyFrame.transformPosition(s.getPosition());
1066
1067
1068 return fromBodyFrame.transformVector(new Vector3D(gradient(date, position, mu)));
1069
1070 }
1071
1072
1073 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
1074 final T[] parameters) {
1075
1076 final T mu = parameters[0];
1077
1078
1079 if (s.getDate().hasZeroField() && isGradientStateDerivative(s) && isGradientConstantOrMuDerivative(mu)) {
1080 @SuppressWarnings("unchecked")
1081 final FieldVector3D<Gradient> p = (FieldVector3D<Gradient>) s.getPosition();
1082 @SuppressWarnings("unchecked")
1083 final FieldVector3D<T> a = (FieldVector3D<T>) accelerationWrtState(s.getDate().toAbsoluteDate(),
1084 s.getFrame(), p,
1085 (Gradient) mu);
1086 return a;
1087 }
1088
1089
1090 final FieldAbsoluteDate<T> date = s.getDate();
1091 final FieldStaticTransform<T> fromBodyFrame = bodyFrame.getStaticTransformTo(s.getFrame(), date);
1092 final FieldStaticTransform<T> toBodyFrame = fromBodyFrame.getInverse();
1093 final FieldVector3D<T> position = toBodyFrame.transformPosition(s.getPosition());
1094
1095
1096 return fromBodyFrame.transformVector(new FieldVector3D<>(gradient(date, position, mu)));
1097
1098 }
1099
1100
1101
1102
1103
1104
1105
1106 private <T extends CalculusFieldElement<T>> boolean isGradientStateDerivative(final FieldSpacecraftState<T> state) {
1107 if (state.getMass() instanceof final Gradient gMass) {
1108 final int p = gMass.getFreeParameters();
1109 if (p < 3) {
1110 return false;
1111 }
1112 @SuppressWarnings("unchecked") final FieldPVCoordinates<Gradient> pv = (FieldPVCoordinates<Gradient>) state.getPVCoordinates();
1113 return isVariable(pv.getPosition().getX(), 0) &&
1114 isVariable(pv.getPosition().getY(), 1) &&
1115 isVariable(pv.getPosition().getZ(), 2);
1116 } else {
1117 return false;
1118 }
1119 }
1120
1121
1122
1123
1124
1125
1126
1127
1128 private <T extends CalculusFieldElement<T>> boolean isGradientConstantOrMuDerivative(final T mu) {
1129 try {
1130 final double[] derivatives = ((Gradient) mu).getGradient();
1131 boolean check = true;
1132 for (int i = 0; i < derivatives.length; i++) {
1133 if (i == 3) {
1134 check &= derivatives[i] == 0.0 || derivatives[i] == 1.0;
1135 } else {
1136 check &= derivatives[i] == 0.0;
1137 }
1138 }
1139 return check;
1140 } catch (ClassCastException cce) {
1141 return false;
1142 }
1143 }
1144
1145
1146
1147
1148
1149
1150
1151 private boolean isVariable(final Gradient g, final int index) {
1152 final double[] derivatives = g.getGradient();
1153 boolean check = true;
1154 for (int i = 0; i < derivatives.length; ++i) {
1155 check &= derivatives[i] == ((index == i) ? 1.0 : 0.0);
1156 }
1157 return check;
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187 private FieldVector3D<Gradient> accelerationWrtState(final AbsoluteDate date, final Frame frame,
1188 final FieldVector3D<Gradient> position,
1189 final Gradient mu) {
1190
1191
1192 final int freeParameters = mu.getFreeParameters();
1193
1194
1195 final StaticTransform fromBodyFrame = bodyFrame.getStaticTransformTo(frame, date);
1196 final StaticTransform toBodyFrame = fromBodyFrame.getInverse();
1197 final Vector3D positionBody = toBodyFrame.transformPosition(position.toVector3D());
1198
1199
1200 final GradientHessian gh = gradientHessian(date, positionBody, mu.getReal());
1201
1202
1203 final double[] gInertial = fromBodyFrame.transformVector(new Vector3D(gh.getGradient())).toArray();
1204
1205
1206 final RealMatrix hBody = new Array2DRowRealMatrix(gh.getHessian(), false);
1207 final RealMatrix rot = new Array2DRowRealMatrix(toBodyFrame.getRotation().getMatrix());
1208 final RealMatrix hInertial = rot.transposeMultiply(hBody).multiply(rot);
1209
1210
1211 final double[] derivatives = new double[freeParameters];
1212 final Gradient[] accDer = new Gradient[3];
1213 for (int i = 0; i < 3; ++i) {
1214
1215
1216 derivatives[0] = hInertial.getEntry(i, 0);
1217 derivatives[1] = hInertial.getEntry(i, 1);
1218 derivatives[2] = hInertial.getEntry(i, 2);
1219
1220
1221 if (derivatives.length > 3 && isVariable(mu, 3)) {
1222 derivatives[3] = gInertial[i] / mu.getReal();
1223 }
1224
1225 accDer[i] = new Gradient(gInertial[i], derivatives);
1226
1227 }
1228
1229 return new FieldVector3D<>(accDer);
1230
1231 }
1232
1233
1234 public List<ParameterDriver> getParametersDrivers() {
1235 return Collections.singletonList(gmParameterDriver);
1236 }
1237
1238 }