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