1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.models.earth.ionosphere.nequick;
18
19 import java.util.Collections;
20 import java.util.List;
21
22 import org.hipparchus.CalculusFieldElement;
23 import org.hipparchus.Field;
24 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.hipparchus.util.FastMath;
27 import org.hipparchus.util.MathArrays;
28 import org.orekit.bodies.BodyShape;
29 import org.orekit.bodies.FieldGeodeticPoint;
30 import org.orekit.bodies.GeodeticPoint;
31 import org.orekit.frames.FieldStaticTransform;
32 import org.orekit.frames.Frame;
33 import org.orekit.frames.StaticTransform;
34 import org.orekit.frames.TopocentricFrame;
35 import org.orekit.models.earth.ionosphere.IonosphericDelayModel;
36 import org.orekit.models.earth.ionosphere.IonosphericModel;
37 import org.orekit.propagation.FieldSpacecraftState;
38 import org.orekit.propagation.SpacecraftState;
39 import org.orekit.time.AbsoluteDate;
40 import org.orekit.time.DateComponents;
41 import org.orekit.time.DateTimeComponents;
42 import org.orekit.time.FieldAbsoluteDate;
43 import org.orekit.time.TimeScale;
44 import org.orekit.utils.ParameterDriver;
45 import org.orekit.utils.units.Unit;
46
47
48
49
50
51
52
53
54
55
56
57
58 public abstract class NeQuickModel implements IonosphericModel, IonosphericDelayModel {
59
60
61 public static final double RE = 6371200.0;
62
63
64 private static final double DENSITY_FACTOR = 1.0e11;
65
66
67 private static final double DELAY_FACTOR = 40.3e16;
68
69
70 private final double[][] flattenF2;
71
72
73 private final double[][] flattenFm3;
74
75
76 private final TimeScale utc;
77
78
79
80
81
82 protected NeQuickModel(final TimeScale utc) {
83
84 this.utc = utc;
85
86
87 this.flattenF2 = new double[12][];
88 this.flattenFm3 = new double[12][];
89
90 }
91
92
93
94
95
96 public TimeScale getUtc() {
97 return utc;
98 }
99
100
101 @Override
102 public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame,
103 final double frequency, final double[] parameters) {
104 return pathDelay(state, baseFrame, state.getDate(), frequency, parameters);
105 }
106
107
108 @Override
109 public double pathDelay(final SpacecraftState state,
110 final TopocentricFrame baseFrame, final AbsoluteDate receptionDate,
111 final double frequency, final double[] parameters) {
112
113
114 final BodyShape ellipsoid = baseFrame.getParentShape();
115
116
117
118
119 final Frame bodyFrame = ellipsoid.getBodyFrame();
120 final StaticTransform body2Inert = bodyFrame.getStaticTransformTo(state.getFrame(), receptionDate);
121 final Vector3D position = body2Inert.getInverse().transformPosition(state.getPosition());
122
123
124 final GeodeticPoint recPoint = baseFrame.getPoint();
125
126 final AbsoluteDate date = state.getDate();
127
128
129 final GeodeticPoint satPoint = ellipsoid.transform(position, bodyFrame, state.getDate());
130
131
132 final double tec = stec(date, recPoint, satPoint);
133
134
135 final double factor = DELAY_FACTOR / (frequency * frequency);
136 return factor * tec;
137 }
138
139
140 @Override
141 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state,
142 final TopocentricFrame baseFrame,
143 final double frequency,
144 final T[] parameters) {
145 return pathDelay(state, baseFrame, state.getDate(), frequency, parameters);
146 }
147
148
149 @Override
150 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state,
151 final TopocentricFrame baseFrame,
152 final FieldAbsoluteDate<T> receptionDate,
153 final double frequency,
154 final T[] parameters) {
155
156 final BodyShape ellipsoid = baseFrame.getParentShape();
157
158
159
160
161 final Frame bodyFrame = ellipsoid.getBodyFrame();
162 final FieldStaticTransform<T> body2Inert = bodyFrame.getStaticTransformTo(state.getFrame(), receptionDate);
163 final FieldVector3D<T> position = body2Inert.getInverse().transformPosition(state.getPosition());
164
165
166 final FieldAbsoluteDate<T> date = state.getDate();
167
168 final FieldGeodeticPoint<T> recPoint = baseFrame.getPoint(date.getField());
169
170
171 final FieldGeodeticPoint<T> satPoint = ellipsoid.transform(position, bodyFrame, state.getDate());
172
173
174 final T tec = stec(date, recPoint, satPoint);
175
176
177 final double factor = DELAY_FACTOR / (frequency * frequency);
178 return tec.multiply(factor);
179 }
180
181
182 @Override
183 public List<ParameterDriver> getParametersDrivers() {
184 return Collections.emptyList();
185 }
186
187
188
189
190
191
192
193
194 public double stec(final AbsoluteDate date, final GeodeticPoint recP, final GeodeticPoint satP) {
195 return stec(date.getComponents(utc), new Ray(recP, satP));
196 }
197
198
199
200
201
202
203
204
205
206 public <T extends CalculusFieldElement<T>> T stec(final FieldAbsoluteDate<T> date,
207 final FieldGeodeticPoint<T> recP,
208 final FieldGeodeticPoint<T> satP) {
209 return stec(date.getComponents(utc), new FieldRay<>(recP, satP));
210 }
211
212
213
214
215
216
217
218 protected abstract double computeMODIP(double latitude, double longitude);
219
220
221
222
223
224
225
226
227 protected abstract <T extends CalculusFieldElement<T>> T computeMODIP(T latitude, T longitude);
228
229
230
231
232
233
234
235
236 public FourierTimeSeries computeFourierTimeSeries(final DateTimeComponents dateTime, final double az) {
237
238
239 loadsIfNeeded(dateTime.getDate());
240
241 return new FourierTimeSeries(dateTime, az,
242 flattenF2[dateTime.getDate().getMonth() - 1],
243 flattenFm3[dateTime.getDate().getMonth() - 1]);
244
245 }
246
247
248
249
250
251
252
253
254
255
256
257 public double electronDensity(final DateTimeComponents dateTime, final double az,
258 final double latitude, final double longitude, final double h) {
259 return electronDensity(computeFourierTimeSeries(dateTime, az), latitude, longitude, h);
260 }
261
262
263
264
265
266
267
268
269
270
271 public double electronDensity(final FourierTimeSeries fourierTimeSeries,
272 final double latitude, final double longitude, final double h) {
273
274 final double modip = computeMODIP(latitude, longitude);
275 final NeQuickParameters parameters = new NeQuickParameters(fourierTimeSeries, latitude, longitude, modip);
276
277
278 final double hInKm = Unit.KILOMETRE.fromSI(h);
279
280
281 final double n;
282 if (hInKm <= parameters.getHmF2()) {
283 n = bottomElectronDensity(hInKm, parameters);
284 } else {
285 n = topElectronDensity(hInKm, parameters);
286 }
287
288 return n;
289
290 }
291
292
293
294
295
296
297
298
299
300 public <T extends CalculusFieldElement<T>> FieldFourierTimeSeries<T> computeFourierTimeSeries(final DateTimeComponents dateTime,
301 final T az) {
302
303
304 loadsIfNeeded(dateTime.getDate());
305
306 return new FieldFourierTimeSeries<>(dateTime, az,
307 flattenF2[dateTime.getDate().getMonth() - 1],
308 flattenFm3[dateTime.getDate().getMonth() - 1]);
309
310 }
311
312
313
314
315
316
317
318
319
320
321
322
323
324 public <T extends CalculusFieldElement<T>> T electronDensity(final DateTimeComponents dateTime, final T az,
325 final T latitude, final T longitude, final T h) {
326 return electronDensity(computeFourierTimeSeries(dateTime, az), latitude, longitude, h);
327 }
328
329
330
331
332
333
334
335
336
337
338
339 public <T extends CalculusFieldElement<T>> T electronDensity(final FieldFourierTimeSeries<T> fourierTimeSeries,
340 final T latitude, final T longitude, final T h) {
341
342 final T modip = computeMODIP(latitude, longitude);
343 final FieldNeQuickParameters<T> parameters = new FieldNeQuickParameters<>(fourierTimeSeries, latitude, longitude, modip);
344
345
346 final T hInKm = Unit.KILOMETRE.fromSI(h);
347
348
349 final T n;
350 if (hInKm.getReal() <= parameters.getHmF2().getReal()) {
351 n = bottomElectronDensity(hInKm, parameters);
352 } else {
353 n = topElectronDensity(hInKm, parameters);
354 }
355
356 return n;
357
358 }
359
360
361
362
363
364
365
366 private double bottomElectronDensity(final double h, final NeQuickParameters parameters) {
367
368
369 final double be = parameters.getBE(h);
370 final double bf1 = parameters.getBF1(h);
371 final double bf2 = parameters.getB2Bot();
372
373
374 final double[] ct = new double[] {
375 1.0 / bf2, 1.0 / bf1, 1.0 / be
376 };
377
378
379 final double[] arguments = computeExponentialArguments(h, parameters);
380
381
382 final double[] s = new double[3];
383
384 final double[] ds = new double[3];
385
386
387 final double[] amplitudes = parameters.getLayerAmplitudes();
388
389
390 for (int i = 0; i < 3; i++) {
391 if (FastMath.abs(arguments[i]) > 25.0) {
392 s[i] = 0.0;
393 ds[i] = 0.0;
394 } else {
395 final double expA = clipExp(arguments[i]);
396 final double opExpA = 1.0 + expA;
397 s[i] = amplitudes[i] * (expA / (opExpA * opExpA));
398 ds[i] = ct[i] * ((1.0 - expA) / (1.0 + expA));
399 }
400 }
401
402
403 final double aNo = s[0] + s[1] + s[2];
404 if (applyChapmanParameters(h)) {
405
406 final double bc = 1.0 - 10.0 * (MathArrays.linearCombination(s[0], ds[0], s[1], ds[1], s[2], ds[2]) / aNo);
407 final double z = 0.1 * (h - 100.0);
408
409 return aNo * clipExp(1.0 - bc * z - clipExp(-z)) * DENSITY_FACTOR;
410 } else {
411 return aNo * DENSITY_FACTOR;
412 }
413
414 }
415
416
417
418
419
420
421
422
423 private <T extends CalculusFieldElement<T>> T bottomElectronDensity(final T h,
424 final FieldNeQuickParameters<T> parameters) {
425
426
427 final Field<T> field = h.getField();
428 final T zero = field.getZero();
429 final T one = field.getOne();
430
431
432 final T be = parameters.getBE(h);
433 final T bf1 = parameters.getBF1(h);
434 final T bf2 = parameters.getB2Bot();
435
436
437 final T[] ct = MathArrays.buildArray(field, 3);
438 ct[0] = bf2.reciprocal();
439 ct[1] = bf1.reciprocal();
440 ct[2] = be.reciprocal();
441
442
443 final T[] arguments = computeExponentialArguments(h, parameters);
444
445
446 final T[] s = MathArrays.buildArray(field, 3);
447
448 final T[] ds = MathArrays.buildArray(field, 3);
449
450
451 final T[] amplitudes = parameters.getLayerAmplitudes();
452
453
454 for (int i = 0; i < 3; i++) {
455 if (FastMath.abs(arguments[i]).getReal() > 25.0) {
456 s[i] = zero;
457 ds[i] = zero;
458 } else {
459 final T expA = clipExp(arguments[i]);
460 final T opExpA = expA.add(1.0);
461 s[i] = amplitudes[i].multiply(expA.divide(opExpA.multiply(opExpA)));
462 ds[i] = ct[i].multiply(expA.negate().add(1.0).divide(expA.add(1.0)));
463 }
464 }
465
466
467 final T aNo = s[0].add(s[1]).add(s[2]);
468 if (applyChapmanParameters(h.getReal())) {
469
470 final T bc = one.linearCombination(s[0], ds[0], s[1], ds[1], s[2], ds[2]).divide(aNo).multiply(10.0).negate().add(1.0);
471 final T z = h.subtract(100.0).multiply(0.1);
472
473 return aNo.multiply(clipExp(bc.multiply(z).add(clipExp(z.negate())).negate().add(1.0))).multiply(DENSITY_FACTOR);
474 } else {
475 return aNo.multiply(DENSITY_FACTOR);
476 }
477
478 }
479
480
481
482
483
484
485
486 private double topElectronDensity(final double h, final NeQuickParameters parameters) {
487
488
489 final double g = 0.125;
490 final double r = 100.0;
491
492
493 final double deltaH = h - parameters.getHmF2();
494 final double h0 = computeH0(parameters);
495 final double z = deltaH / (h0 * (1.0 + (r * g * deltaH) / (r * h0 + g * deltaH)));
496
497
498 final double ee = clipExp(z);
499
500
501 if (ee > 1.0e11) {
502 return (4.0 * parameters.getNmF2() / ee) * DENSITY_FACTOR;
503 } else {
504 final double opExpZ = 1.0 + ee;
505 return ((4.0 * parameters.getNmF2() * ee) / (opExpZ * opExpZ)) * DENSITY_FACTOR;
506 }
507 }
508
509
510
511
512
513
514
515
516 private <T extends CalculusFieldElement<T>> T topElectronDensity(final T h,
517 final FieldNeQuickParameters<T> parameters) {
518
519
520 final double g = 0.125;
521 final double r = 100.0;
522
523
524 final T deltaH = h.subtract(parameters.getHmF2());
525 final T h0 = computeH0(parameters);
526 final T z = deltaH.divide(h0.multiply(deltaH.multiply(r).multiply(g).divide(h0.multiply(r).add(deltaH.multiply(g))).add(1.0)));
527
528
529 final T ee = clipExp(z);
530
531
532 if (ee.getReal() > 1.0e11) {
533 return parameters.getNmF2().multiply(4.0).divide(ee).multiply(DENSITY_FACTOR);
534 } else {
535 final T opExpZ = ee.add(1.0);
536 return parameters.getNmF2().multiply(4.0).multiply(ee).divide(opExpZ.multiply(opExpZ)).multiply(DENSITY_FACTOR);
537 }
538 }
539
540
541
542
543
544 private void loadsIfNeeded(final DateComponents date) {
545
546
547 final int monthIndex = date.getMonth() - 1;
548
549
550 if (flattenF2[monthIndex] == null) {
551
552
553 final CCIRLoader loader = new CCIRLoader();
554 loader.loadCCIRCoefficients(date);
555
556
557 this.flattenF2[monthIndex] = flatten(loader.getF2());
558 this.flattenFm3[monthIndex] = flatten(loader.getFm3());
559 }
560 }
561
562
563
564
565
566
567
568
569
570 private double[] flatten(final double[][][] original) {
571 final double[] flatten = new double[original.length * original[0].length * original[0][0].length];
572 int index = 0;
573 for (int j = 0; j < original[0].length; j++) {
574 for (int k = 0; k < original[0][0].length; k++) {
575 for (final double[][] doubles : original) {
576 flatten[index++] = doubles[j][k];
577 }
578 }
579 }
580 return flatten;
581 }
582
583
584
585
586
587
588
589
590
591
592 protected double clipExp(final double power) {
593 if (power > 80.0) {
594 return 5.5406E34;
595 } else if (power < -80) {
596 return 1.8049E-35;
597 } else {
598 return FastMath.exp(power);
599 }
600 }
601
602
603
604
605
606
607
608
609
610
611
612 protected <T extends CalculusFieldElement<T>> T clipExp(final T power) {
613 if (power.getReal() > 80.0) {
614 return power.newInstance(5.5406E34);
615 } else if (power.getReal() < -80) {
616 return power.newInstance(1.8049E-35);
617 } else {
618 return FastMath.exp(power);
619 }
620 }
621
622
623
624
625
626
627
628
629 abstract double stec(DateTimeComponents dateTime, Ray ray);
630
631
632
633
634
635
636
637
638
639 abstract <T extends CalculusFieldElement<T>> T stec(DateTimeComponents dateTime, FieldRay<T> ray);
640
641
642
643
644
645
646
647
648 abstract boolean applyChapmanParameters(double hInKm);
649
650
651
652
653
654
655
656
657 abstract double[] computeExponentialArguments(double h, NeQuickParameters parameters);
658
659
660
661
662
663
664
665
666
667 abstract <T extends CalculusFieldElement<T>> T[] computeExponentialArguments(T h,
668 FieldNeQuickParameters<T> parameters);
669
670
671
672
673
674
675
676 abstract double computeH0(NeQuickParameters parameters);
677
678
679
680
681
682
683
684
685 abstract <T extends CalculusFieldElement<T>> T computeH0(FieldNeQuickParameters<T> parameters);
686
687 }