1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.models.earth.atmosphere;
18
19 import java.util.stream.IntStream;
20 import org.hipparchus.CalculusFieldElement;
21 import org.hipparchus.Field;
22 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23 import org.hipparchus.geometry.euclidean.threed.Vector3D;
24 import org.hipparchus.util.FastMath;
25 import org.hipparchus.util.MathArrays;
26 import org.hipparchus.util.MathUtils;
27 import org.orekit.bodies.BodyShape;
28 import org.orekit.bodies.FieldGeodeticPoint;
29 import org.orekit.bodies.GeodeticPoint;
30 import org.orekit.errors.OrekitException;
31 import org.orekit.errors.OrekitMessages;
32 import org.orekit.frames.Frame;
33 import org.orekit.time.AbsoluteDate;
34 import org.orekit.time.DateTimeComponents;
35 import org.orekit.time.FieldAbsoluteDate;
36 import org.orekit.time.TimeScale;
37 import org.orekit.utils.Constants;
38 import org.orekit.utils.ExtendedPositionProvider;
39
40
41
42
43
44
45
46
47
48
49 public abstract class AbstractJacchiaBowmanModel extends AbstractSunInfluencedAtmosphere {
50
51
52 private static final double ALT_MIN = 90000.;
53
54
55 private static final double[] ALPHA = {0, 0, 0, 0, -0.38};
56
57
58 private static final double LOG10 = FastMath.log(10.);
59
60
61 private static final double[] AMW = {28.0134, 31.9988, 15.9994, 39.9480, 4.0026, 1.00797};
62
63
64 private static final double AVOGAD = 6.02257e26;
65
66
67 private static final double[] FRAC = {0.78110, 0.20955, 9.3400e-3, 1.2890e-5};
68
69
70 private static final double RSTAR = 8.31432;
71
72
73 private static final double R1 = 0.010;
74
75
76 private static final double R2 = 0.025;
77
78
79 private static final double R3 = 0.075;
80
81
82 private static final double[] WT = {0.311111111111111, 1.422222222222222, 0.533333333333333, 1.422222222222222, 0.311111111111111};
83
84
85 private static final double EARTH_RADIUS = 6356.766;
86
87
88 private static final double[] BDT_SUB = {-0.457512297e+01, -0.512114909e+01, -0.693003609e+02,
89 0.203716701e+03, 0.703316291e+03, -0.194349234e+04,
90 0.110651308e+04, -0.174378996e+03, 0.188594601e+04,
91 -0.709371517e+04, 0.922454523e+04, -0.384508073e+04,
92 -0.645841789e+01, 0.409703319e+02, -0.482006560e+03,
93 0.181870931e+04, -0.237389204e+04, 0.996703815e+03,
94 0.361416936e+02};
95
96
97 private static final double[] CDT_SUB = {-0.155986211e+02, -0.512114909e+01, -0.693003609e+02,
98 0.203716701e+03, 0.703316291e+03, -0.194349234e+04,
99 0.110651308e+04, -0.220835117e+03, 0.143256989e+04,
100 -0.318481844e+04, 0.328981513e+04, -0.135332119e+04,
101 0.199956489e+02, -0.127093998e+02, 0.212825156e+02,
102 -0.275555432e+01, 0.110234982e+02, 0.148881951e+03,
103 -0.751640284e+03, 0.637876542e+03, 0.127093998e+02,
104 -0.212825156e+02, 0.275555432e+01};
105
106
107 private static final double[] CXAMB = {28.15204, -8.5586e-2, +1.2840e-4, -1.0056e-5, -1.0210e-5, +1.5044e-6, +9.9826e-8};
108
109
110 private static final double[] CHT = {0.22, -0.20e-02, 0.115e-02, -0.211e-05};
111
112
113 private final TimeScale utc;
114
115
116 private final BodyShape earth;
117
118
119 private final AbsoluteDate minDataEpoch;
120
121
122 private final AbsoluteDate maxDataEpoch;
123
124
125
126
127
128
129
130
131
132
133 protected AbstractJacchiaBowmanModel(final ExtendedPositionProvider sun, final TimeScale utc, final BodyShape earth,
134 final AbsoluteDate minDataEpoch, final AbsoluteDate maxDataEpoch) {
135 super(sun);
136 this.utc = utc;
137 this.earth = earth;
138 this.minDataEpoch = minDataEpoch;
139 this.maxDataEpoch = maxDataEpoch;
140 }
141
142
143
144
145 public TimeScale getUtc() {
146 return utc;
147 }
148
149
150
151
152 public BodyShape getEarth() {
153 return earth;
154 }
155
156
157 @Override
158 public double getDensity(final AbsoluteDate date, final Vector3D position, final Frame frame) {
159
160
161 if (date.compareTo(maxDataEpoch) > 0 || date.compareTo(minDataEpoch) < 0) {
162 throw new OrekitException(OrekitMessages.NO_SOLAR_ACTIVITY_AT_DATE, date, minDataEpoch, maxDataEpoch);
163 }
164
165
166 final GeodeticPoint inBody = earth.transform(position, frame, date);
167
168
169 final Frame ecef = getFrame();
170 final Vector3D sunPos = getSunPosition(date, ecef);
171 final GeodeticPoint sunInBody = earth.transform(sunPos, ecef, date);
172 return computeDensity(date, sunInBody.getLongitude(), sunInBody.getLatitude(), inBody.getLongitude(), inBody.getLatitude(), inBody.getAltitude());
173 }
174
175
176 @Override
177 public <T extends CalculusFieldElement<T>> T getDensity(final FieldAbsoluteDate<T> date, final FieldVector3D<T> position, final Frame frame) {
178
179
180 final AbsoluteDate dateD = date.toAbsoluteDate();
181 if (dateD.compareTo(maxDataEpoch) > 0 || dateD.compareTo(minDataEpoch) < 0) {
182 throw new OrekitException(OrekitMessages.NO_SOLAR_ACTIVITY_AT_DATE, dateD, minDataEpoch, maxDataEpoch);
183 }
184
185
186 final FieldGeodeticPoint<T> inBody = earth.transform(position, frame, date);
187
188
189 final Frame ecef = getFrame();
190 final FieldVector3D<T> sunPos = getSunPosition(date, ecef);
191 final FieldGeodeticPoint<T> sunInBody = earth.transform(sunPos, ecef, date);
192 return computeDensity(date,
193 sunInBody.getLongitude(), sunInBody.getLatitude(),
194 inBody.getLongitude(), inBody.getLatitude(), inBody.getAltitude());
195 }
196
197
198 @Override
199 public Frame getFrame() {
200 return earth.getBodyFrame();
201 }
202
203
204
205
206
207
208
209
210
211
212 protected double computeDensity(final AbsoluteDate date,
213 final double sunRA, final double sunDecli,
214 final double satLon, final double satLat, final double satAlt) {
215
216 if (satAlt < ALT_MIN) {
217 throw new OrekitException(OrekitMessages.ALTITUDE_BELOW_ALLOWED_THRESHOLD, satAlt, ALT_MIN);
218 }
219 final double altKm = satAlt / 1000.0;
220
221 final DateTimeComponents dt = date.getComponents(utc);
222 final double dateMJD = dt.getDate().getMJD() +
223 dt.getTime().getSecondsInLocalDay() / Constants.JULIAN_DAY;
224
225
226
227 final double tsubc = computeTc(date);
228
229
230 final double eta = 0.5 * FastMath.abs(satLat - sunDecli);
231 final double theta = 0.5 * FastMath.abs(satLat + sunDecli);
232
233
234 final double h = satLon - sunRA;
235 final double tau = h - 0.64577182 + 0.10471976 * FastMath.sin(h + 0.75049158);
236 final double solarTime = solarTimeHour(h);
237
238
239 final double tsubl = tSubL(eta, theta, tau, tsubc);
240
241
242 final double dtclst = dTc(getF10(date), solarTime, satLat, altKm);
243
244
245 final double tInf = computeTInf(date, tsubl, dtclst);
246
247
248 final double tsubx = tSubX(tInf);
249
250
251 final double gsubx = gSubX(tsubx);
252
253
254 final double[] tc = tSubCArray(tsubx, gsubx, tInf);
255
256
257 final double z1 = 90.;
258 final double z2 = FastMath.min(altKm, 105.0);
259 double al = FastMath.log(z2 / z1);
260 int n = (int) FastMath.floor(al / R1) + 1;
261 double zr = FastMath.exp(al / n);
262 final double mb1 = mBar(z1);
263 final double tloc1 = localTemp(z1, tc);
264 double zend = z1;
265 double sub2 = 0.;
266 double ain = mb1 * gravity(z1) / tloc1;
267 double mb2 = 0;
268 double tloc2 = 0;
269 double z = 0;
270 double gravl = 0;
271
272 for (int i = 0; i < n; ++i) {
273 z = zend;
274 zend = zr * z;
275 final double dz = 0.25 * (zend - z);
276 double sum1 = WT[0] * ain;
277 for (int j = 1; j < 5; ++j) {
278 z += dz;
279 mb2 = mBar(z);
280 tloc2 = localTemp(z, tc);
281 gravl = gravity(z);
282 ain = mb2 * gravl / tloc2;
283 sum1 += WT[j] * ain;
284 }
285 sub2 += dz * sum1;
286 }
287 double rho = 3.46e-6 * mb2 * tloc1 / FastMath.exp(sub2 / RSTAR) / (mb1 * tloc2);
288
289
290 final double anm = AVOGAD * rho;
291 final double an = anm / mb2;
292
293
294 double fact2 = anm / 28.960;
295 final double[] aln = new double[6];
296 aln[0] = FastMath.log(FRAC[0] * fact2);
297 aln[3] = FastMath.log(FRAC[2] * fact2);
298 aln[4] = FastMath.log(FRAC[3] * fact2);
299
300 aln[1] = FastMath.log(fact2 * (1. + FRAC[1]) - an);
301 aln[2] = FastMath.log(2. * (an - fact2));
302
303 if (altKm <= 105.0) {
304
305 aln[5] = aln[4] - 25.0;
306 }
307 else {
308
309 al = FastMath.log(FastMath.min(altKm, 500.0) / z);
310 n = 1 + (int) FastMath.floor(al / R2);
311 zr = FastMath.exp(al / n);
312 sub2 = 0.;
313 ain = gravl / tloc2;
314
315 double tloc3 = 0;
316 for (int i = 0; i < n; ++i) {
317 z = zend;
318 zend = zr * z;
319 final double dz = 0.25 * (zend - z);
320 double SUM1 = WT[0] * ain;
321 for (int j = 1; j < 5; ++j) {
322 z += dz;
323 tloc3 = localTemp(z, tc);
324 gravl = gravity(z);
325 ain = gravl / tloc3;
326 SUM1 += WT[j] * ain;
327 }
328 sub2 += dz * SUM1;
329 }
330
331 al = FastMath.log(FastMath.max(altKm, 500.0) / z);
332 final double r = (altKm > 500.0) ? R3 : R2;
333 n = 1 + (int) FastMath.floor(al / r);
334 zr = FastMath.exp(al / n);
335 double sum3 = 0.;
336 double tloc4 = 0;
337 for (int i = 0; i < n; ++i) {
338 z = zend;
339 zend = zr * z;
340 final double dz = 0.25 * (zend - z);
341 double sum1 = WT[0] * ain;
342 for (int j = 1; j < 5; ++j) {
343 z += dz;
344 tloc4 = localTemp(z, tc);
345 gravl = gravity(z);
346 ain = gravl / tloc4;
347 sum1 = sum1 + WT[j] * ain;
348 }
349 sum3 = sum3 + dz * sum1;
350 }
351 final double altr;
352 final double hSign;
353 if (altKm <= 500.) {
354 altr = FastMath.log(tloc3 / tloc2);
355 fact2 = sub2 / RSTAR;
356 hSign = 1.0;
357 }
358 else {
359 altr = FastMath.log(tloc4 / tloc2);
360 fact2 = (sub2 + sum3) / RSTAR;
361 hSign = -1.0;
362 }
363 for (int i = 0; i < 5; ++i) {
364 aln[i] = aln[i] - (1.0 + ALPHA[i]) * altr - fact2 * AMW[i];
365 }
366
367
368 final double al10t5 = FastMath.log10(tInf);
369 final double alnh5 = (5.5 * al10t5 - 39.40) * al10t5 + 73.13;
370 aln[5] = LOG10 * (alnh5 + 6.) + hSign * (FastMath.log(tloc4 / tloc3) + sum3 * AMW[5] / RSTAR);
371 }
372
373
374 final double dlrsl = dlrsl(altKm, dateMJD, satLat);
375
376
377 double dlrsa = 0;
378 if (z < 2000.0) {
379
380 dlrsa = semian(date, dayOfYear(dateMJD), altKm);
381 }
382
383
384
385
386
387 final double dlr = LOG10 * (dlrsl + dlrsa);
388 for (int i = 0; i < 6; ++i) {
389 aln[i] += dlr;
390 }
391
392
393
394 final double sumnm = IntStream.range(0, 6).mapToDouble(i -> FastMath.exp(aln[i]) * AMW[i]).sum();
395 rho = sumnm / AVOGAD;
396
397
398 final double fex = densityCorrectionFactor(altKm, getF10B(date));
399
400
401 rho *= fex;
402
403 return rho;
404 }
405
406
407
408
409
410
411
412
413
414
415
416 protected <T extends CalculusFieldElement<T>> T computeDensity(final FieldAbsoluteDate<T> date,
417 final T sunRA, final T sunDecli,
418 final T satLon, final T satLat, final T satAlt) {
419
420 if (satAlt.getReal() < ALT_MIN) {
421 throw new OrekitException(OrekitMessages.ALTITUDE_BELOW_ALLOWED_THRESHOLD, satAlt.getReal(), ALT_MIN);
422 }
423
424 final AbsoluteDate dateR = date.toAbsoluteDate();
425 final DateTimeComponents components = date.getComponents(utc);
426 final T dateMJD = date.durationFrom(new FieldAbsoluteDate<>(date.getField(), components, utc))
427 .add(components.getTime().getSecondsInLocalDay())
428 .divide(Constants.JULIAN_DAY)
429 .add(components.getDate().getMJD());
430
431 final Field<T> field = satAlt.getField();
432 final T altKm = satAlt.divide(1000.0);
433
434
435 final double tsubc = computeTc(dateR);
436
437
438 final T eta = FastMath.abs(satLat.subtract(sunDecli)).multiply(0.5);
439 final T theta = FastMath.abs(satLat.add(sunDecli)).multiply(0.5);
440
441
442 final T h = satLon.subtract(sunRA);
443 final T tau = h.subtract(0.64577182).add(h.add(0.75049158).sin().multiply(0.10471976));
444 final T solarTime = solarTimeHour(h);
445
446
447 final T tsubl = tSubL(eta, theta, tau, tsubc);
448
449
450 final T dtclst = dTc(getF10(dateR), solarTime, satLat, altKm);
451
452
453 final T tinf = computeTInf(dateR, tsubl, dtclst);
454
455
456 final T tsubx = tSubX(tinf);
457
458
459 final T gsubx = gSubX(tsubx);
460
461
462 final T[] tc = tSubCArray(tsubx, gsubx, tinf, field);
463
464
465 final T z1 = field.getZero().newInstance(90.);
466 final T z2 = FastMath.min(altKm, 105.0);
467 T al = z2.divide(z1).log();
468 int n = 1 + (int) FastMath.floor(al.getReal() / R1);
469 T zr = al.divide(n).exp();
470 final T mb1 = mBar(z1);
471 final T tloc1 = localTemp(z1, tc);
472 T zend = z1;
473 T sub2 = field.getZero();
474 T ain = mb1.multiply(gravity(z1)).divide(tloc1);
475 T mb2 = field.getZero();
476 T tloc2 = field.getZero();
477 T z = field.getZero();
478 T gravl = field.getZero();
479
480 for (int i = 0; i < n; ++i) {
481 z = zend;
482 zend = zr.multiply(z);
483 final T dz = zend.subtract(z).multiply(0.25);
484 T sum1 = ain.multiply(WT[0]);
485 for (int j = 1; j < 5; ++j) {
486 z = z.add(dz);
487 mb2 = mBar(z);
488 tloc2 = localTemp(z, tc);
489 gravl = gravity(z);
490 ain = mb2.multiply(gravl).divide(tloc2);
491 sum1 = sum1.add(ain.multiply(WT[j]));
492 }
493 sub2 = sub2.add(dz.multiply(sum1));
494 }
495 T rho = mb2.multiply(3.46e-6).multiply(tloc1).divide(sub2.divide(RSTAR).exp().multiply(mb1.multiply(tloc2)));
496
497
498 final T anm = rho.multiply(AVOGAD);
499 final T an = anm.divide(mb2);
500
501
502 T fact2 = anm.divide(28.960);
503 final T[] aln = MathArrays.buildArray(field, 6);
504 aln[0] = fact2.multiply(FRAC[0]).log();
505 aln[3] = fact2.multiply(FRAC[2]).log();
506 aln[4] = fact2.multiply(FRAC[3]).log();
507
508
509 aln[1] = fact2.multiply(1. + FRAC[1]).subtract(an).log();
510 aln[2] = an.subtract(fact2).multiply(2).log();
511
512 if (altKm.getReal() <= 105.0) {
513
514 aln[5] = aln[4].subtract(25.0);
515 }
516 else {
517
518 al = FastMath.min(altKm, 500.0).divide(z).log();
519 n = 1 + (int) FastMath.floor(al.getReal() / R2);
520 zr = al.divide(n).exp();
521 sub2 = field.getZero();
522 ain = gravl.divide(tloc2);
523
524 T tloc3 = field.getZero();
525 for (int i = 0; i < n; ++i) {
526 z = zend;
527 zend = zr.multiply(z);
528 final T dz = zend.subtract(z).multiply(0.25);
529 T sum1 = ain.multiply(WT[0]);
530 for (int j = 1; j < 5; ++j) {
531 z = z.add(dz);
532 tloc3 = localTemp(z, tc);
533 gravl = gravity(z);
534 ain = gravl.divide(tloc3);
535 sum1 = sum1.add(ain.multiply(WT[j]));
536 }
537 sub2 = sub2.add(dz.multiply(sum1));
538 }
539
540 al = FastMath.max(altKm, 500.0).divide(z).log();
541 final double r = (altKm.getReal() > 500.0) ? R3 : R2;
542 n = 1 + (int) FastMath.floor(al.getReal() / r);
543 zr = al.divide(n).exp();
544 T sum3 = field.getZero();
545 T tloc4 = field.getZero();
546 for (int i = 0; i < n; ++i) {
547 z = zend;
548 zend = zr.multiply(z);
549 final T dz = zend.subtract(z).multiply(0.25);
550 T sum1 = ain.multiply(WT[0]);
551 for (int j = 1; j < 5; ++j) {
552 z = z.add(dz);
553 tloc4 = localTemp(z, tc);
554 gravl = gravity(z);
555 ain = gravl.divide(tloc4);
556 sum1 = sum1.add(ain.multiply(WT[j]));
557 }
558 sum3 = sum3.add(dz.multiply(sum1));
559 }
560 final T altr;
561 final double hSign;
562 if (altKm.getReal() <= 500.) {
563 altr = tloc3.divide(tloc2).log();
564 fact2 = sub2.divide(RSTAR);
565 hSign = 1.0;
566 }
567 else {
568 altr = tloc4.divide(tloc2).log();
569 fact2 = sub2.add(sum3).divide(RSTAR);
570 hSign = -1.0;
571 }
572 for (int i = 0; i < 5; ++i) {
573 aln[i] = aln[i].subtract(altr.multiply(1.0 + ALPHA[i])).subtract(fact2.multiply(AMW[i]));
574 }
575
576
577 final T al10t5 = tinf.log10();
578 final T alnh5 = al10t5.multiply(5.5).subtract(39.40).multiply(al10t5).add(73.13);
579 aln[5] = alnh5.add(6.).multiply(LOG10).add(tloc4.divide(tloc3).log().add(sum3.multiply(AMW[5] / RSTAR)).multiply(hSign));
580 }
581
582
583 final T dlrsl = dlrsl(altKm, dateMJD, satLat);
584
585
586 T dlrsa = field.getZero();
587 if (z.getReal() < 2000.0) {
588
589 dlrsa = semian(dateR, dayOfYear(dateMJD), altKm);
590 }
591
592
593
594
595
596 final T dlr = dlrsl.add(dlrsa).multiply(LOG10);
597 for (int i = 0; i < 6; ++i) {
598 aln[i] = aln[i].add(dlr);
599 }
600
601
602
603 T sumnm = field.getZero();
604 for (int i = 0; i < 6; ++i) {
605 sumnm = sumnm.add(aln[i].exp().multiply(AMW[i]));
606 }
607 rho = sumnm.divide(AVOGAD);
608
609
610 final T fex = densityCorrectionFactor(altKm, getF10B(dateR), field);
611
612
613 rho = rho.multiply(fex);
614
615 return rho;
616 }
617
618
619
620
621
622
623
624 protected abstract double semian(AbsoluteDate date, double day, double altKm);
625
626
627
628
629
630
631
632
633 protected abstract double computeTInf(AbsoluteDate date, double tsubl, double dtclst);
634
635
636
637
638
639 protected abstract double computeTc(AbsoluteDate date);
640
641
642
643
644
645 protected abstract double getF10(AbsoluteDate date);
646
647
648
649
650
651 protected abstract double getF10B(AbsoluteDate date);
652
653
654
655
656
657
658
659
660 protected abstract <T extends CalculusFieldElement<T>> T semian(AbsoluteDate date, T day, T altKm);
661
662
663
664
665
666
667
668
669
670 protected abstract <T extends CalculusFieldElement<T>> T computeTInf(AbsoluteDate date, T tsubl, T dtclst);
671
672
673
674
675
676 private static double solarTimeHour(final double h) {
677 final double solTimeHour = FastMath.toDegrees(h + FastMath.PI) / 15.0;
678 if (solTimeHour >= 24) {
679 return solTimeHour - 24.;
680 }
681 if (solTimeHour < 0) {
682 return solTimeHour + 24.;
683 }
684 return solTimeHour;
685 }
686
687
688
689
690
691
692 private static <T extends CalculusFieldElement<T>> T solarTimeHour(final T h) {
693 final T solarTime = FastMath.toDegrees(h.add(FastMath.PI)).divide(15.0);
694 if (solarTime.getReal() >= 24) {
695 return solarTime.subtract(24);
696 }
697 if (solarTime.getReal() < 0) {
698 return solarTime.add(24);
699 }
700 return solarTime;
701 }
702
703
704
705
706
707
708
709
710
711
712
713
714 private static double tSubL(final double eta, final double theta,
715 final double tau, final double tsubc) {
716 final double cosEta = FastMath.pow(FastMath.cos(eta), 2.5);
717 final double sinTheta = FastMath.pow(FastMath.sin(theta), 2.5);
718 final double cosTau = FastMath.abs(FastMath.cos(0.5 * tau));
719 final double df = sinTheta + (cosEta - sinTheta) * cosTau * cosTau * cosTau;
720 return tsubc * (1. + 0.31 * df);
721 }
722
723
724
725
726
727
728
729
730
731
732
733
734
735 private static <T extends CalculusFieldElement<T>> T tSubL(final T eta, final T theta,
736 final T tau, final double tsubc) {
737 final T cos = eta.cos();
738 final T cosEta = cos.square().multiply(cos.sqrt());
739 final T sin = theta.sin();
740 final T sinTheta = sin.square().multiply(sin.sqrt());
741 final T cosTau = tau.multiply(0.5).cos().abs();
742 final T df = sinTheta.add(cosEta.subtract(sinTheta).multiply(cosTau).multiply(cosTau).multiply(cosTau));
743 return df.multiply(0.31).add(1).multiply(tsubc);
744 }
745
746
747
748
749
750
751
752
753
754 private static double tSubX(final double tInf) {
755 return 444.3807 + 0.02385 * tInf - 392.8292 * FastMath.exp(-0.0021357 * tInf);
756 }
757
758
759
760
761
762
763
764
765
766
767 private static <T extends CalculusFieldElement<T>> T tSubX(final T tInf) {
768 return tInf.multiply(0.02385).add(444.3807).subtract(tInf.multiply(-0.0021357).exp().multiply(392.8292));
769 }
770
771
772
773
774
775 private static double gSubX(final double tSubX) {
776 return 0.054285714 * (tSubX - 183.);
777 }
778
779
780
781
782
783
784 private static <T extends CalculusFieldElement<T>> T gSubX(final T tSubX) {
785 return tSubX.subtract(183.).multiply(0.054285714);
786 }
787
788
789
790
791
792
793
794
795
796
797 private static double[] tSubCArray(final double tSubX, final double gSubX,
798 final double tInf) {
799 final double[] tc = new double[4];
800 tc[0] = tSubX;
801 tc[1] = gSubX;
802 tc[2] = (tInf - tSubX) / MathUtils.SEMI_PI;
803 tc[3] = gSubX / tc[2];
804 return tc;
805 }
806
807
808
809
810
811
812
813
814
815
816
817
818 private static <T extends CalculusFieldElement<T>> T[] tSubCArray(final T tSubX, final T gSubX,
819 final T tInf, final Field<T> field) {
820 final T[] tc = MathArrays.buildArray(field, 4);
821 tc[0] = tSubX;
822 tc[1] = gSubX;
823 tc[2] = tInf.subtract(tSubX).divide(MathUtils.SEMI_PI);
824 tc[3] = gSubX.divide(tc[2]);
825 return tc;
826 }
827
828
829
830
831
832
833 private static double localTemp(final double z, final double[] tc) {
834 final double dz = z - 125;
835 if (dz <= 0) {
836 return ((-9.8204695e-6 * dz - 7.3039742e-4) * dz * dz + 1.0) * dz * tc[1] + tc[0];
837 }
838 else {
839 return tc[0] + tc[2] * FastMath.atan(tc[3] * dz * (1 + 4.5e-6 * FastMath.pow(dz, 2.5)));
840 }
841 }
842
843
844
845
846
847
848
849 private static <T extends CalculusFieldElement<T>> T localTemp(final T z, final T[] tc) {
850 final T dz = z.subtract(125.);
851 if (dz.getReal() <= 0.) {
852 return dz.multiply(-9.8204695e-6).subtract(7.3039742e-4).multiply(dz).multiply(dz).add(1.0).multiply(dz).multiply(tc[1]).add(tc[0]);
853 } else {
854 return dz.multiply(dz).multiply(dz.sqrt()).multiply(4.5e-6).add(1).multiply(dz).multiply(tc[3]).atan().multiply(tc[2]).add(tc[0]);
855 }
856 }
857
858
859
860
861
862 private static double mBar(final double z) {
863 final double dz = z - 100.;
864 double amb = CXAMB[6];
865 for (int i = 5; i >= 0; --i) {
866 amb = dz * amb + CXAMB[i];
867 }
868 return amb;
869 }
870
871
872
873
874
875
876 private static <T extends CalculusFieldElement<T>> T mBar(final T z) {
877 final T dz = z.subtract(100.);
878 T amb = z.getField().getZero().newInstance(CXAMB[6]);
879 for (int i = 5; i >= 0; --i) {
880 amb = dz.multiply(amb).add(CXAMB[i]);
881 }
882 return amb;
883 }
884
885
886
887
888
889 private static double gravity(final double z) {
890 final double temp = 1.0 + z / EARTH_RADIUS;
891 return Constants.G0_STANDARD_GRAVITY / (temp * temp);
892 }
893
894
895
896
897
898
899 private static <T extends CalculusFieldElement<T>> T gravity(final T z) {
900 final T tmp = z.divide(EARTH_RADIUS).add(1);
901 return tmp.multiply(tmp).reciprocal().multiply(Constants.G0_STANDARD_GRAVITY);
902 }
903
904
905
906
907
908 private static double dayOfYear(final double dateMJD) {
909 final double d1950 = dateMJD - 33281;
910
911 int iyday = (int) d1950;
912 final double frac = d1950 - iyday;
913 iyday = iyday + 364;
914
915 int itemp = iyday / 1461;
916
917 iyday = iyday - itemp * 1461;
918 itemp = iyday / 365;
919 if (itemp >= 3) {
920 itemp = 3;
921 }
922 iyday = iyday - 365 * itemp + 1;
923 return iyday + frac;
924 }
925
926
927
928
929
930
931 private static <T extends CalculusFieldElement<T>> T dayOfYear(final T dateMJD) {
932 final T d1950 = dateMJD.subtract(33281);
933
934 int iyday = (int) d1950.getReal();
935 final T frac = d1950.subtract(iyday);
936 iyday = iyday + 364;
937
938 int itemp = iyday / 1461;
939
940 iyday = iyday - itemp * 1461;
941 itemp = iyday / 365;
942 if (itemp >= 3) {
943 itemp = 3;
944 }
945 iyday = iyday - 365 * itemp + 1;
946 return frac.add(iyday);
947 }
948
949
950
951
952
953
954
955 private static double dlrsl(final double altKm, final double dateMJD, final double satLat) {
956 final double capPhi = ((dateMJD - 36204.0) / 365.2422) % 1;
957 final int signum = (satLat >= 0) ? 1 : -1;
958 final double sinLat = FastMath.sin(satLat);
959 final double hm90 = altKm - 90.;
960 return 0.02 * hm90 * FastMath.exp(-0.045 * hm90) * signum * FastMath.sin(MathUtils.TWO_PI * capPhi + 1.72) * sinLat * sinLat;
961 }
962
963
964
965
966
967
968
969
970 private static <T extends CalculusFieldElement<T>> T dlrsl(final T altKm, final T dateMJD, final T satLat) {
971 T capPhi = dateMJD.subtract(36204.0).divide(365.2422);
972 capPhi = capPhi.subtract(FastMath.floor(capPhi.getReal()));
973 final int signum = (satLat.getReal() >= 0.) ? 1 : -1;
974 final T sinLat = satLat.sin();
975 final T hm90 = altKm.subtract(90.);
976 return hm90.multiply(0.02).multiply(hm90.multiply(-0.045).exp()).multiply(capPhi.multiply(MathUtils.TWO_PI).add(1.72).sin()).multiply(signum).multiply(sinLat).multiply(sinLat);
977 }
978
979
980
981
982
983
984 private static double densityCorrectionFactor(final double altKm, final double f10B) {
985 if (altKm >= 1000.0 && altKm < 1500.0) {
986 final double zeta = (altKm - 1000.) * 0.002;
987 final double f15c = CHT[0] + CHT[1] * f10B + CHT[2] * 1500.0 + CHT[3] * f10B * 1500.0;
988 final double f15cZeta = (CHT[2] + CHT[3] * f10B) * 500.0;
989 final double fex2 = 3.0 * f15c - f15cZeta - 3.0;
990 final double fex3 = f15cZeta - 2.0 * f15c + 2.0;
991 return 1.0 + zeta * zeta * (fex2 + fex3 * zeta);
992 }
993 if (altKm >= 1500.0) {
994 return CHT[0] + CHT[1] * f10B + CHT[2] * altKm + CHT[3] * f10B * altKm;
995 }
996 return 1.0;
997 }
998
999
1000
1001
1002
1003
1004
1005
1006 private static <T extends CalculusFieldElement<T>> T densityCorrectionFactor(final T altKm, final double f10B,
1007 final Field<T> field) {
1008 if (altKm.getReal() >= 1000.0 && altKm.getReal() < 1500.0) {
1009 final T zeta = altKm.subtract(1000.).multiply(0.002);
1010 final double f15c = CHT[0] + CHT[1] * f10B + CHT[2] * 1500.0 + CHT[3] * f10B * 1500.0;
1011 final double f15cZeta = (CHT[2] + CHT[3] * f10B) * 500.0;
1012 final double fex2 = 3.0 * f15c - f15cZeta - 3.0;
1013 final double fex3 = f15cZeta - 2.0 * f15c + 2.0;
1014 return field.getOne().add(zeta.multiply(zeta).multiply(zeta.multiply(fex3).add(fex2)));
1015 }
1016 if (altKm.getReal() >= 1500.0) {
1017 return altKm.multiply(CHT[3] * f10B).add(altKm.multiply(CHT[2])).add(CHT[0] + CHT[1] * f10B);
1018 }
1019 return field.getOne();
1020 }
1021
1022
1023
1024
1025
1026
1027
1028
1029 private static double dTc(final double f10, final double solarTime,
1030 final double satLat, final double satAlt) {
1031 final double st = solarTime / 24.0;
1032 final double cs = FastMath.cos(satLat);
1033 final double fs = (f10 - 100.0) / 100.0;
1034
1035
1036 if (satAlt >= 120 && satAlt <= 200) {
1037 final double dtc200 = poly2CDTC(fs, st, cs);
1038 final double dtc200dz = poly1CDTC(fs, st, cs);
1039 final double cc = 3.0 * dtc200 - dtc200dz;
1040 final double dd = dtc200 - cc;
1041 final double zp = (satAlt - 120.0) / 80.0;
1042 return zp * zp * (cc + dd * zp);
1043
1044 } else if (satAlt > 200.0 && satAlt <= 240.0) {
1045 final double h = (satAlt - 200.0) / 50.0;
1046 return poly1CDTC(fs, st, cs) * h + poly2CDTC(fs, st, cs);
1047
1048 } else if (satAlt > 240.0 && satAlt <= 300.0) {
1049 final double h = 0.8;
1050 final double bb = poly1CDTC(fs, st, cs);
1051 final double aa = bb * h + poly2CDTC(fs, st, cs);
1052 final double p2BDT = poly2BDTC(st);
1053 final double dtc300 = poly1BDTC(fs, st, cs, 3 * p2BDT);
1054 final double dtc300dz = cs * p2BDT;
1055 final double cc = 3.0 * dtc300 - dtc300dz - 3.0 * aa - 2.0 * bb;
1056 final double dd = dtc300 - aa - bb - cc;
1057 final double zp = (satAlt - 240.0) / 60.0;
1058 return aa + zp * (bb + zp * (cc + zp * dd));
1059
1060 } else if (satAlt > 300.0 && satAlt <= 600.0) {
1061 final double h = satAlt / 100.0;
1062 return poly1BDTC(fs, st, cs, h * poly2BDTC(st));
1063
1064 } else if (satAlt > 600.0 && satAlt <= 800.0) {
1065 final double poly2 = poly2BDTC(st);
1066 final double aa = poly1BDTC(fs, st, cs, 6 * poly2);
1067 final double bb = cs * poly2;
1068 final double cc = -(3.0 * aa + 4.0 * bb) / 4.0;
1069 final double dd = (aa + bb) / 4.0;
1070 final double zp = (satAlt - 600.0) / 100.0;
1071 return aa + zp * (bb + zp * (cc + zp * dd));
1072
1073 }
1074 return 0.;
1075 }
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 private static <T extends CalculusFieldElement<T>> T dTc(final double f10, final T solarTime,
1086 final T satLat, final T satAlt) {
1087 final T st = solarTime.divide(24.0);
1088 final T cs = satLat.cos();
1089 final double fs = (f10 - 100.0) / 100.0;
1090
1091
1092 if (satAlt.getReal() >= 120 && satAlt.getReal() <= 200) {
1093 final T dtc200 = poly2CDTC(fs, st, cs);
1094 final T dtc200dz = poly1CDTC(fs, st, cs);
1095 final T cc = dtc200.multiply(3).subtract(dtc200dz);
1096 final T dd = dtc200.subtract(cc);
1097 final T zp = satAlt.subtract(120.0).divide(80.0);
1098 return zp.multiply(zp).multiply(cc.add(dd.multiply(zp)));
1099
1100 } else if (satAlt.getReal() > 200.0 && satAlt.getReal() <= 240.0) {
1101 final T h = satAlt.subtract(200.0).divide(50.0);
1102 return poly1CDTC(fs, st, cs).multiply(h).add(poly2CDTC(fs, st, cs));
1103
1104 } else if (satAlt.getReal() > 240.0 && satAlt.getReal() <= 300.0) {
1105 final T h = solarTime.getField().getZero().newInstance(0.8);
1106 final T bb = poly1CDTC(fs, st, cs);
1107 final T aa = bb.multiply(h).add(poly2CDTC(fs, st, cs));
1108 final T p2BDT = poly2BDTC(st);
1109 final T dtc300 = poly1BDTC(fs, st, cs, p2BDT.multiply(3));
1110 final T dtc300dz = cs.multiply(p2BDT);
1111 final T cc = dtc300.multiply(3).subtract(dtc300dz).subtract(aa.multiply(3)).subtract(bb.multiply(2));
1112 final T dd = dtc300.subtract(aa).subtract(bb).subtract(cc);
1113 final T zp = satAlt.subtract(240.0).divide(60.0);
1114 return aa.add(zp.multiply(bb.add(zp.multiply(cc.add(zp.multiply(dd))))));
1115
1116 } else if (satAlt.getReal() > 300.0 && satAlt.getReal() <= 600.0) {
1117 final T h = satAlt.divide(100.0);
1118 return poly1BDTC(fs, st, cs, h.multiply(poly2BDTC(st)));
1119
1120 } else if (satAlt.getReal() > 600.0 && satAlt.getReal() <= 800.0) {
1121 final T poly2 = poly2BDTC(st);
1122 final T aa = poly1BDTC(fs, st, cs, poly2.multiply(6));
1123 final T bb = cs.multiply(poly2);
1124 final T cc = aa.multiply(3).add(bb.multiply(4)).divide(-4.0);
1125 final T dd = aa.add(bb).divide(4.0);
1126 final T zp = satAlt.subtract(600.0).divide(100.0);
1127 return aa.add(zp.multiply(bb.add(zp.multiply(cc.add(zp.multiply(dd))))));
1128
1129 }
1130 return satLat.getField().getZero();
1131 }
1132
1133
1134
1135
1136
1137
1138
1139 private static double poly1CDTC(final double fs, final double st, final double cs) {
1140 return CDT_SUB[0] +
1141 fs * (CDT_SUB[1] + st * (CDT_SUB[2] + st * (CDT_SUB[3] + st * (CDT_SUB[4] + st * (CDT_SUB[5] + st * CDT_SUB[6]))))) +
1142 cs * st * (CDT_SUB[7] + st * (CDT_SUB[8] + st * (CDT_SUB[9] + st * (CDT_SUB[10] + st * CDT_SUB[11])))) +
1143 cs * (CDT_SUB[12] + fs * (CDT_SUB[13] + st * (CDT_SUB[14] + st * CDT_SUB[15])));
1144 }
1145
1146
1147
1148
1149
1150
1151
1152
1153 private static <T extends CalculusFieldElement<T>> T poly1CDTC(final double fs, final T st, final T cs) {
1154 return st.multiply(CDT_SUB[6]).
1155 add(CDT_SUB[5]).multiply(st).
1156 add(CDT_SUB[4]).multiply(st).
1157 add(CDT_SUB[3]).multiply(st).
1158 add(CDT_SUB[2]).multiply(st).
1159 add(CDT_SUB[1]).multiply(fs).
1160 add(st.multiply(CDT_SUB[11]).
1161 add(CDT_SUB[10]).multiply(st).
1162 add(CDT_SUB[ 9]).multiply(st).
1163 add(CDT_SUB[ 8]).multiply(st).
1164 add(CDT_SUB[7]).multiply(st).multiply(cs)).
1165 add(st.multiply(CDT_SUB[15]).
1166 add(CDT_SUB[14]).multiply(st).
1167 add(CDT_SUB[13]).multiply(fs).
1168 add(CDT_SUB[12]).multiply(cs)).
1169 add(CDT_SUB[0]);
1170 }
1171
1172
1173
1174
1175
1176
1177
1178 private static double poly2CDTC(final double fs, final double st, final double cs) {
1179 return CDT_SUB[16] + st * cs * (CDT_SUB[17] + st * (CDT_SUB[18] + st * CDT_SUB[19])) +
1180 fs * cs * (CDT_SUB[20] + st * (CDT_SUB[21] + st * CDT_SUB[22]));
1181 }
1182
1183
1184
1185
1186
1187
1188
1189
1190 private static <T extends CalculusFieldElement<T>> T poly2CDTC(final double fs, final T st, final T cs) {
1191 return st.multiply(CDT_SUB[19]).
1192 add(CDT_SUB[18]).multiply(st).
1193 add(CDT_SUB[17]).multiply(cs).multiply(st).
1194 add(st.multiply(CDT_SUB[22]).
1195 add(CDT_SUB[21]).multiply(st).
1196 add(CDT_SUB[20]).multiply(cs).multiply(fs)).
1197 add(CDT_SUB[16]);
1198 }
1199
1200
1201
1202
1203
1204
1205
1206
1207 private static double poly1BDTC(final double fs, final double st, final double cs, final double hp) {
1208 return BDT_SUB[0] +
1209 fs * (BDT_SUB[1] + st * (BDT_SUB[2] + st * (BDT_SUB[3] + st * (BDT_SUB[4] + st * (BDT_SUB[5] + st * BDT_SUB[6]))))) +
1210 cs * (st * (BDT_SUB[7] + st * (BDT_SUB[8] + st * (BDT_SUB[9] + st * (BDT_SUB[10] + st * BDT_SUB[11])))) + hp + BDT_SUB[18]);
1211 }
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 private static <T extends CalculusFieldElement<T>> T poly1BDTC(final double fs, final T st, final T cs, final T hp) {
1222 return st.multiply(BDT_SUB[6]).
1223 add(BDT_SUB[5]).multiply(st).
1224 add(BDT_SUB[4]).multiply(st).
1225 add(BDT_SUB[3]).multiply(st).
1226 add(BDT_SUB[2]).multiply(st).
1227 add(BDT_SUB[1]).multiply(fs).
1228 add(st.multiply(BDT_SUB[11]).
1229 add(BDT_SUB[10]).multiply(st).
1230 add(BDT_SUB[ 9]).multiply(st).
1231 add(BDT_SUB[ 8]).multiply(st).
1232 add(BDT_SUB[ 7]).multiply(st).
1233 add(hp).add(BDT_SUB[18]).multiply(cs)).
1234 add(BDT_SUB[0]);
1235 }
1236
1237
1238
1239
1240
1241 private static double poly2BDTC(final double st) {
1242 return BDT_SUB[12] + st * (BDT_SUB[13] + st * (BDT_SUB[14] + st * (BDT_SUB[15] + st * (BDT_SUB[16] + st * BDT_SUB[17]))));
1243 }
1244
1245
1246
1247
1248
1249
1250 private static <T extends CalculusFieldElement<T>> T poly2BDTC(final T st) {
1251 return st.multiply(BDT_SUB[17]).
1252 add(BDT_SUB[16]).multiply(st).
1253 add(BDT_SUB[15]).multiply(st).
1254 add(BDT_SUB[14]).multiply(st).
1255 add(BDT_SUB[13]).multiply(st).
1256 add(BDT_SUB[12]);
1257 }
1258
1259 }