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