1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.models.earth.ionosphere;
18
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.InputStreamReader;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.regex.Pattern;
27
28 import org.hipparchus.CalculusFieldElement;
29 import org.hipparchus.Field;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.FieldSinCos;
32 import org.hipparchus.util.MathArrays;
33 import org.hipparchus.util.SinCos;
34 import org.orekit.annotation.DefaultDataContext;
35 import org.orekit.bodies.BodyShape;
36 import org.orekit.bodies.FieldGeodeticPoint;
37 import org.orekit.bodies.GeodeticPoint;
38 import org.orekit.data.DataContext;
39 import org.orekit.errors.OrekitException;
40 import org.orekit.errors.OrekitMessages;
41 import org.orekit.frames.TopocentricFrame;
42 import org.orekit.propagation.FieldSpacecraftState;
43 import org.orekit.propagation.SpacecraftState;
44 import org.orekit.time.AbsoluteDate;
45 import org.orekit.time.DateComponents;
46 import org.orekit.time.DateTimeComponents;
47 import org.orekit.time.FieldAbsoluteDate;
48 import org.orekit.time.TimeScale;
49 import org.orekit.utils.ParameterDriver;
50
51
52
53
54
55
56
57
58
59
60
61 public class NeQuickModel implements IonosphericModel {
62
63
64 private static final String NEQUICK_BASE = "/assets/org/orekit/nequick/";
65
66
67 private static final Pattern SEPARATOR = Pattern.compile("\\s+");
68
69
70 private static final double RE = 6371200.0;
71
72
73 private static final double M_TO_KM = 0.001;
74
75
76 private static final double DENSITY_FACTOR = 1.0e11;
77
78
79 private static final double DELAY_FACTOR = 40.3e16;
80
81
82 private final double[] alpha;
83
84
85 private final double[][] stModip;
86
87
88 private int month;
89
90
91 private double[][][] f2;
92
93
94 private double[][][] fm3;
95
96
97 private final TimeScale utc;
98
99
100
101
102
103
104
105
106
107 @DefaultDataContext
108 public NeQuickModel(final double[] alpha) {
109 this(alpha, DataContext.getDefault().getTimeScales().getUTC());
110 }
111
112
113
114
115
116
117
118 public NeQuickModel(final double[] alpha,
119 final TimeScale utc) {
120
121 this.month = 0;
122 this.f2 = null;
123 this.fm3 = null;
124
125 final MODIPLoader parser = new MODIPLoader();
126 parser.loadMODIPGrid();
127 this.stModip = parser.getMODIPGrid();
128
129 this.alpha = alpha.clone();
130 this.utc = utc;
131 }
132
133 @Override
134 public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame,
135 final double frequency, final double[] parameters) {
136
137 final GeodeticPoint recPoint = baseFrame.getPoint();
138
139 final AbsoluteDate date = state.getDate();
140
141
142 final BodyShape ellipsoid = baseFrame.getParentShape();
143
144 final GeodeticPoint satPoint = ellipsoid.transform(state.getPosition(ellipsoid.getBodyFrame()), ellipsoid.getBodyFrame(), state.getDate());
145
146
147 final double tec = stec(date, recPoint, satPoint);
148
149
150 final double factor = DELAY_FACTOR / (frequency * frequency);
151 return factor * tec;
152 }
153
154 @Override
155 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state, final TopocentricFrame baseFrame,
156 final double frequency, final T[] parameters) {
157
158 final FieldAbsoluteDate<T> date = state.getDate();
159
160 final FieldGeodeticPoint<T> recPoint = baseFrame.getPoint(date.getField());
161
162
163
164 final BodyShape ellipsoid = baseFrame.getParentShape();
165
166 final FieldGeodeticPoint<T> satPoint = ellipsoid.transform(state.getPosition(ellipsoid.getBodyFrame()), ellipsoid.getBodyFrame(), state.getDate());
167
168
169 final T tec = stec(date, recPoint, satPoint);
170
171
172 final double factor = DELAY_FACTOR / (frequency * frequency);
173 return tec.multiply(factor);
174 }
175
176 @Override
177 public List<ParameterDriver> getParametersDrivers() {
178 return Collections.emptyList();
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192 public double stec(final AbsoluteDate date, final GeodeticPoint recP, final GeodeticPoint satP) {
193
194
195 final Ray ray = new Ray(recP, satP);
196
197
198 final DateTimeComponents dateTime = date.getComponents(utc);
199 loadsIfNeeded(dateTime.getDate());
200
201
202 final double h1 = recP.getAltitude();
203 final double tolerance;
204 if (h1 < 1000000.0) {
205 tolerance = 0.001;
206 } else {
207 tolerance = 0.01;
208 }
209
210
211 int n = 8;
212 final Segment seg1 = new Segment(n, ray);
213 double gn1 = stecIntegration(seg1, dateTime);
214 n *= 2;
215 final Segment seg2 = new Segment(n, ray);
216 double gn2 = stecIntegration(seg2, dateTime);
217
218 int count = 1;
219 while (FastMath.abs(gn2 - gn1) > tolerance * FastMath.abs(gn1) && count < 20) {
220 gn1 = gn2;
221 n *= 2;
222 final Segment seg = new Segment(n, ray);
223 gn2 = stecIntegration(seg, dateTime);
224 count += 1;
225 }
226
227
228 if (count == 20) {
229 throw new OrekitException(OrekitMessages.STEC_INTEGRATION_DID_NOT_CONVERGE);
230 }
231
232
233 return (gn2 + ((gn2 - gn1) / 15.0)) * 1.0e-16;
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248 public <T extends CalculusFieldElement<T>> T stec(final FieldAbsoluteDate<T> date,
249 final FieldGeodeticPoint<T> recP,
250 final FieldGeodeticPoint<T> satP) {
251
252
253 final Field<T> field = date.getField();
254
255
256 final FieldRay<T> ray = new FieldRay<>(field, recP, satP);
257
258
259 final DateTimeComponents dateTime = date.getComponents(utc);
260 loadsIfNeeded(dateTime.getDate());
261
262
263 final T h1 = recP.getAltitude();
264 final double tolerance;
265 if (h1.getReal() < 1000000.0) {
266 tolerance = 0.001;
267 } else {
268 tolerance = 0.01;
269 }
270
271
272 int n = 8;
273 final FieldSegment<T> seg1 = new FieldSegment<>(field, n, ray);
274 T gn1 = stecIntegration(field, seg1, dateTime);
275 n *= 2;
276 final FieldSegment<T> seg2 = new FieldSegment<>(field, n, ray);
277 T gn2 = stecIntegration(field, seg2, dateTime);
278
279 int count = 1;
280 while (FastMath.abs(gn2.subtract(gn1)).getReal() > FastMath.abs(gn1).multiply(tolerance).getReal() && count < 20) {
281 gn1 = gn2;
282 n *= 2;
283 final FieldSegment<T> seg = new FieldSegment<>(field, n, ray);
284 gn2 = stecIntegration(field, seg, dateTime);
285 count += 1;
286 }
287
288
289 if (count == 20) {
290 throw new OrekitException(OrekitMessages.STEC_INTEGRATION_DID_NOT_CONVERGE);
291 }
292
293
294 return gn2.add(gn2.subtract(gn1).divide(15.0)).multiply(1.0e-16);
295 }
296
297
298
299
300
301
302
303 private double stecIntegration(final Segment seg, final DateTimeComponents dateTime) {
304
305 final double[] heightS = seg.getHeights();
306 final double[] latitudeS = seg.getLatitudes();
307 final double[] longitudeS = seg.getLongitudes();
308
309
310 double density = 0.0;
311 for (int i = 0; i < heightS.length; i++) {
312 final NeQuickParameters parameters = new NeQuickParameters(dateTime, f2, fm3,
313 latitudeS[i], longitudeS[i],
314 alpha, stModip);
315 density += electronDensity(heightS[i], parameters);
316 }
317
318 return 0.5 * seg.getInterval() * density;
319 }
320
321
322
323
324
325
326
327
328
329 private <T extends CalculusFieldElement<T>> T stecIntegration(final Field<T> field,
330 final FieldSegment<T> seg,
331 final DateTimeComponents dateTime) {
332
333 final T[] heightS = seg.getHeights();
334 final T[] latitudeS = seg.getLatitudes();
335 final T[] longitudeS = seg.getLongitudes();
336
337
338 T density = field.getZero();
339 for (int i = 0; i < heightS.length; i++) {
340 final FieldNeQuickParameters<T> parameters = new FieldNeQuickParameters<>(field, dateTime, f2, fm3,
341 latitudeS[i], longitudeS[i],
342 alpha, stModip);
343 density = density.add(electronDensity(field, heightS[i], parameters));
344 }
345
346 return seg.getInterval().multiply(density).multiply(0.5);
347 }
348
349
350
351
352
353
354
355 private double electronDensity(final double h, final NeQuickParameters parameters) {
356
357 final double hInKm = h * M_TO_KM;
358
359 final double n;
360 if (hInKm <= parameters.getHmF2()) {
361 n = bottomElectronDensity(hInKm, parameters);
362 } else {
363 n = topElectronDensity(hInKm, parameters);
364 }
365 return n;
366 }
367
368
369
370
371
372
373
374
375
376 private <T extends CalculusFieldElement<T>> T electronDensity(final Field<T> field,
377 final T h,
378 final FieldNeQuickParameters<T> parameters) {
379
380 final T hInKm = h.multiply(M_TO_KM);
381
382 final T n;
383 if (hInKm.getReal() <= parameters.getHmF2().getReal()) {
384 n = bottomElectronDensity(field, hInKm, parameters);
385 } else {
386 n = topElectronDensity(field, hInKm, parameters);
387 }
388 return n;
389 }
390
391
392
393
394
395
396
397 private double bottomElectronDensity(final double h, final NeQuickParameters parameters) {
398
399
400 final double be;
401 if (h > parameters.getHmE()) {
402 be = parameters.getBETop();
403 } else {
404 be = parameters.getBEBot();
405 }
406 final double bf1;
407 if (h > parameters.getHmF1()) {
408 bf1 = parameters.getB1Top();
409 } else {
410 bf1 = parameters.getB1Bot();
411 }
412 final double bf2 = parameters.getB2Bot();
413
414
415 final double[] ct = new double[] {
416 1.0 / bf2, 1.0 / bf1, 1.0 / be
417 };
418
419
420
421 final double hTemp = FastMath.max(100.0, h);
422 final double exp = clipExp(10.0 / (1.0 + FastMath.abs(hTemp - parameters.getHmF2())));
423 final double[] arguments = new double[3];
424 arguments[0] = (hTemp - parameters.getHmF2()) / bf2;
425 arguments[1] = ((hTemp - parameters.getHmF1()) / bf1) * exp;
426 arguments[2] = ((hTemp - parameters.getHmE()) / be) * exp;
427
428
429 final double[] s = new double[3];
430
431 final double[] ds = new double[3];
432
433
434 final double[] amplitudes = parameters.getLayerAmplitudes();
435
436
437 for (int i = 0; i < 3; i++) {
438 if (FastMath.abs(arguments[i]) > 25.0) {
439 s[i] = 0.0;
440 ds[i] = 0.0;
441 } else {
442 final double expA = clipExp(arguments[i]);
443 final double opExpA = 1.0 + expA;
444 s[i] = amplitudes[i] * (expA / (opExpA * opExpA));
445 ds[i] = ct[i] * ((1.0 - expA) / (1.0 + expA));
446 }
447 }
448
449
450 final double aNo = MathArrays.linearCombination(s[0], 1.0, s[1], 1.0, s[2], 1.0);
451 if (h >= 100) {
452 return aNo * DENSITY_FACTOR;
453 } else {
454
455 final double bc = 1.0 - 10.0 * (MathArrays.linearCombination(s[0], ds[0], s[1], ds[1], s[2], ds[2]) / aNo);
456 final double z = 0.1 * (h - 100.0);
457
458 return aNo * clipExp(1.0 - bc * z - clipExp(-z)) * DENSITY_FACTOR;
459 }
460 }
461
462
463
464
465
466
467
468
469
470 private <T extends CalculusFieldElement<T>> T bottomElectronDensity(final Field<T> field,
471 final T h,
472 final FieldNeQuickParameters<T> parameters) {
473
474
475 final T zero = field.getZero();
476 final T one = field.getOne();
477
478
479 final T be;
480 if (h.getReal() > parameters.getHmE().getReal()) {
481 be = parameters.getBETop();
482 } else {
483 be = parameters.getBEBot();
484 }
485 final T bf1;
486 if (h.getReal() > parameters.getHmF1().getReal()) {
487 bf1 = parameters.getB1Top();
488 } else {
489 bf1 = parameters.getB1Bot();
490 }
491 final T bf2 = parameters.getB2Bot();
492
493
494 final T[] ct = MathArrays.buildArray(field, 3);
495 ct[0] = bf2.reciprocal();
496 ct[1] = bf1.reciprocal();
497 ct[2] = be.reciprocal();
498
499
500
501 final T hTemp = FastMath.max(zero.newInstance(100.0), h);
502 final T exp = clipExp(field, FastMath.abs(hTemp.subtract(parameters.getHmF2())).add(1.0).divide(10.0).reciprocal());
503 final T[] arguments = MathArrays.buildArray(field, 3);
504 arguments[0] = hTemp.subtract(parameters.getHmF2()).divide(bf2);
505 arguments[1] = hTemp.subtract(parameters.getHmF1()).divide(bf1).multiply(exp);
506 arguments[2] = hTemp.subtract(parameters.getHmE()).divide(be).multiply(exp);
507
508
509 final T[] s = MathArrays.buildArray(field, 3);
510
511 final T[] ds = MathArrays.buildArray(field, 3);
512
513
514 final T[] amplitudes = parameters.getLayerAmplitudes();
515
516
517 for (int i = 0; i < 3; i++) {
518 if (FastMath.abs(arguments[i]).getReal() > 25.0) {
519 s[i] = zero;
520 ds[i] = zero;
521 } else {
522 final T expA = clipExp(field, arguments[i]);
523 final T opExpA = expA.add(1.0);
524 s[i] = amplitudes[i].multiply(expA.divide(opExpA.multiply(opExpA)));
525 ds[i] = ct[i].multiply(expA.negate().add(1.0).divide(expA.add(1.0)));
526 }
527 }
528
529
530 final T aNo = one.linearCombination(s[0], one, s[1], one, s[2], one);
531 if (h.getReal() >= 100) {
532 return aNo.multiply(DENSITY_FACTOR);
533 } else {
534
535 final T bc = s[0].multiply(ds[0]).add(one.linearCombination(s[0], ds[0], s[1], ds[1], s[2], ds[2])).divide(aNo).multiply(10.0).negate().add(1.0);
536 final T z = h.subtract(100.0).multiply(0.1);
537
538 return aNo.multiply(clipExp(field, bc.multiply(z).add(clipExp(field, z.negate())).negate().add(1.0))).multiply(DENSITY_FACTOR);
539 }
540 }
541
542
543
544
545
546
547
548 private double topElectronDensity(final double h, final NeQuickParameters parameters) {
549
550
551 final double g = 0.125;
552 final double r = 100.0;
553
554
555 final double deltaH = h - parameters.getHmF2();
556 final double z = deltaH / (parameters.getH0() * (1.0 + (r * g * deltaH) / (r * parameters.getH0() + g * deltaH)));
557
558
559 final double ee = clipExp(z);
560
561
562 if (ee > 1.0e11) {
563 return (4.0 * parameters.getNmF2() / ee) * DENSITY_FACTOR;
564 } else {
565 final double opExpZ = 1.0 + ee;
566 return ((4.0 * parameters.getNmF2() * ee) / (opExpZ * opExpZ)) * DENSITY_FACTOR;
567 }
568 }
569
570
571
572
573
574
575
576
577
578 private <T extends CalculusFieldElement<T>> T topElectronDensity(final Field<T> field,
579 final T h,
580 final FieldNeQuickParameters<T> parameters) {
581
582
583 final double g = 0.125;
584 final double r = 100.0;
585
586
587 final T deltaH = h.subtract(parameters.getHmF2());
588 final T z = deltaH.divide(parameters.getH0().multiply(deltaH.multiply(r).multiply(g).divide(parameters.getH0().multiply(r).add(deltaH.multiply(g))).add(1.0)));
589
590
591 final T ee = clipExp(field, z);
592
593
594 if (ee.getReal() > 1.0e11) {
595 return parameters.getNmF2().multiply(4.0).divide(ee).multiply(DENSITY_FACTOR);
596 } else {
597 final T opExpZ = ee.add(field.getOne());
598 return parameters.getNmF2().multiply(4.0).multiply(ee).divide(opExpZ.multiply(opExpZ)).multiply(DENSITY_FACTOR);
599 }
600 }
601
602
603
604
605
606 private void loadsIfNeeded(final DateComponents date) {
607
608
609 final int currentMonth = date.getMonth();
610
611
612 if (currentMonth != month || f2 == null || fm3 == null) {
613 this.month = currentMonth;
614
615
616 final CCIRLoader loader = new CCIRLoader();
617 loader.loadCCIRCoefficients(date);
618
619
620 this.f2 = loader.getF2();
621 this.fm3 = loader.getFm3();
622 }
623 }
624
625
626
627
628
629
630
631
632
633
634 private double clipExp(final double power) {
635 if (power > 80.0) {
636 return 5.5406E34;
637 } else if (power < -80) {
638 return 1.8049E-35;
639 } else {
640 return FastMath.exp(power);
641 }
642 }
643
644
645
646
647
648
649
650
651
652
653
654
655 private <T extends CalculusFieldElement<T>> T clipExp(final Field<T> field, final T power) {
656 final T zero = field.getZero();
657 if (power.getReal() > 80.0) {
658 return zero.newInstance(5.5406E34);
659 } else if (power.getReal() < -80) {
660 return zero.newInstance(1.8049E-35);
661 } else {
662 return FastMath.exp(power);
663 }
664 }
665
666
667
668
669
670 private static InputStream getStream(final String name) {
671 return NeQuickModel.class.getResourceAsStream(name);
672 }
673
674
675
676
677
678
679
680
681
682
683
684
685 private static class MODIPLoader {
686
687
688 private static final String SUPPORTED_NAME = NEQUICK_BASE + "modip.txt";
689
690
691 private double[][] grid;
692
693
694
695
696 MODIPLoader() {
697 this.grid = null;
698 }
699
700
701
702
703 public double[][] getMODIPGrid() {
704 return grid.clone();
705 }
706
707
708
709
710 public void loadMODIPGrid() {
711 try (InputStream in = getStream(SUPPORTED_NAME)) {
712 loadData(in, SUPPORTED_NAME);
713 } catch (IOException e) {
714 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
715 }
716
717
718 if (grid == null) {
719 throw new OrekitException(OrekitMessages.MODIP_GRID_NOT_LOADED, SUPPORTED_NAME);
720 }
721 }
722
723
724
725
726
727
728
729 public void loadData(final InputStream input, final String name)
730 throws IOException {
731
732
733 final int size = 39;
734
735
736 final double[][] array = new double[size][size];
737
738
739 int lineNumber = 0;
740 String line = null;
741 try (InputStreamReader isr = new InputStreamReader(input, StandardCharsets.UTF_8);
742 BufferedReader br = new BufferedReader(isr)) {
743
744 for (line = br.readLine(); line != null; line = br.readLine()) {
745 ++lineNumber;
746 line = line.trim();
747
748
749 if (line.length() > 0) {
750 final String[] modip_line = SEPARATOR.split(line);
751 for (int column = 0; column < modip_line.length; column++) {
752 array[lineNumber - 1][column] = Double.parseDouble(modip_line[column]);
753 }
754 }
755
756 }
757
758 } catch (NumberFormatException nfe) {
759 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
760 lineNumber, name, line);
761 }
762
763
764 grid = array.clone();
765
766 }
767 }
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785 private static class CCIRLoader {
786
787
788 public static final String DEFAULT_SUPPORTED_NAME = "ccir**.asc";
789
790
791 private static final int NUMBER_F2_COEFFICIENTS = 1976;
792
793
794 private static final int ROWS = 2;
795
796
797 private static final int TOTAL_COLUMNS_F2 = 76;
798
799
800 private static final int TOTAL_COLUMNS_FM3 = 49;
801
802
803 private static final int DEPTH_F2 = 13;
804
805
806 private static final int DEPTH_FM3 = 9;
807
808
809 private String supportedName;
810
811
812 private double[][][] f2Loader;
813
814
815 private double[][][] fm3Loader;
816
817
818
819
820 CCIRLoader() {
821 this.supportedName = DEFAULT_SUPPORTED_NAME;
822 this.f2Loader = null;
823 this.fm3Loader = null;
824 }
825
826
827
828
829
830 public double[][][] getF2() {
831 return f2Loader.clone();
832 }
833
834
835
836
837
838 public double[][][] getFm3() {
839 return fm3Loader.clone();
840 }
841
842
843
844
845 public void loadCCIRCoefficients(final DateComponents dateComponents) {
846
847
848 final int currentMonth = dateComponents.getMonth();
849 this.supportedName = NEQUICK_BASE + String.format("ccir%02d.asc", currentMonth + 10);
850 try (InputStream in = getStream(supportedName)) {
851 loadData(in, supportedName);
852 } catch (IOException e) {
853 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
854 }
855
856 if (f2Loader == null || fm3Loader == null) {
857 throw new OrekitException(OrekitMessages.NEQUICK_F2_FM3_NOT_LOADED, supportedName);
858 }
859
860 }
861
862
863
864
865
866
867
868 public void loadData(final InputStream input, final String name)
869 throws IOException {
870
871
872 final double[][][] f2Temp = new double[ROWS][TOTAL_COLUMNS_F2][DEPTH_F2];
873 final double[][][] fm3Temp = new double[ROWS][TOTAL_COLUMNS_FM3][DEPTH_FM3];
874
875
876 int lineNumber = 0;
877 int index = 0;
878 int currentRowF2 = 0;
879 int currentColumnF2 = 0;
880 int currentDepthF2 = 0;
881 int currentRowFm3 = 0;
882 int currentColumnFm3 = 0;
883 int currentDepthFm3 = 0;
884 String line = null;
885
886 try (InputStreamReader isr = new InputStreamReader(input, StandardCharsets.UTF_8);
887 BufferedReader br = new BufferedReader(isr)) {
888
889 for (line = br.readLine(); line != null; line = br.readLine()) {
890 ++lineNumber;
891 line = line.trim();
892
893
894 if (line.length() > 0) {
895 final String[] ccir_line = SEPARATOR.split(line);
896 for (int i = 0; i < ccir_line.length; i++) {
897
898 if (index < NUMBER_F2_COEFFICIENTS) {
899
900 if (currentDepthF2 >= DEPTH_F2 && currentColumnF2 < (TOTAL_COLUMNS_F2 - 1)) {
901 currentDepthF2 = 0;
902 currentColumnF2++;
903 } else if (currentDepthF2 >= DEPTH_F2 && currentColumnF2 >= (TOTAL_COLUMNS_F2 - 1)) {
904 currentDepthF2 = 0;
905 currentColumnF2 = 0;
906 currentRowF2++;
907 }
908 f2Temp[currentRowF2][currentColumnF2][currentDepthF2++] = Double.parseDouble(ccir_line[i]);
909 index++;
910 } else {
911
912 if (currentDepthFm3 >= DEPTH_FM3 && currentColumnFm3 < (TOTAL_COLUMNS_FM3 - 1)) {
913 currentDepthFm3 = 0;
914 currentColumnFm3++;
915 } else if (currentDepthFm3 >= DEPTH_FM3 && currentColumnFm3 >= (TOTAL_COLUMNS_FM3 - 1)) {
916 currentDepthFm3 = 0;
917 currentColumnFm3 = 0;
918 currentRowFm3++;
919 }
920 fm3Temp[currentRowFm3][currentColumnFm3][currentDepthFm3++] = Double.parseDouble(ccir_line[i]);
921 index++;
922 }
923
924 }
925 }
926
927 }
928
929 } catch (NumberFormatException nfe) {
930 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
931 lineNumber, name, line);
932 }
933
934 f2Loader = f2Temp.clone();
935 fm3Loader = fm3Temp.clone();
936
937 }
938
939 }
940
941
942
943
944
945 private static class Ray {
946
947
948 private static final double THRESHOLD = 1.0e-10;
949
950
951 private final double s1;
952
953
954 private final double s2;
955
956
957 private final double rp;
958
959
960 private final double latP;
961
962
963 private final double lonP;
964
965
966 private final SinCos scLatP;
967
968
969 private final double sinAzP;
970
971
972 private final double cosAzP;
973
974
975
976
977
978
979 Ray(final GeodeticPoint recP, final GeodeticPoint satP) {
980
981
982 final double r1 = RE + recP.getAltitude();
983 final double r2 = RE + satP.getAltitude();
984
985
986 final double lat1 = recP.getLatitude();
987 final double lat2 = satP.getLatitude();
988 final double lon1 = recP.getLongitude();
989 final double lon2 = satP.getLongitude();
990 final SinCos scLatSat = FastMath.sinCos(lat2);
991 final SinCos scLatRec = FastMath.sinCos(lat1);
992 final SinCos scLon21 = FastMath.sinCos(lon2 - lon1);
993
994
995 final double cosD = scLatRec.sin() * scLatSat.sin() +
996 scLatRec.cos() * scLatSat.cos() * scLon21.cos();
997 final double sinD = FastMath.sqrt(1.0 - cosD * cosD);
998 final double z = FastMath.atan2(sinD, cosD - (r1 / r2));
999
1000
1001 this.rp = r1 * FastMath.sin(z);
1002
1003
1004 if (FastMath.abs(FastMath.abs(lat1) - 0.5 * FastMath.PI) < THRESHOLD) {
1005
1006
1007 if (lat1 < 0) {
1008 this.latP = -z;
1009 } else {
1010 this.latP = z;
1011 }
1012
1013
1014 if (z < 0) {
1015 this.lonP = lon2;
1016 } else {
1017 this.lonP = lon2 + FastMath.PI;
1018 }
1019
1020 } else {
1021
1022
1023 final double deltaP = 0.5 * FastMath.PI - z;
1024 final SinCos scDeltaP = FastMath.sinCos(deltaP);
1025 final double sinAz = scLon21.sin() * scLatSat.cos() / sinD;
1026 final double cosAz = (scLatSat.sin() - cosD * scLatRec.sin()) / (sinD * scLatRec.cos());
1027 final double sinLatP = scLatRec.sin() * scDeltaP.cos() - scLatRec.cos() * scDeltaP.sin() * cosAz;
1028 final double cosLatP = FastMath.sqrt(1.0 - sinLatP * sinLatP);
1029 this.latP = FastMath.atan2(sinLatP, cosLatP);
1030
1031
1032 final double sinLonP = -sinAz * scDeltaP.sin() / cosLatP;
1033 final double cosLonP = (scDeltaP.cos() - scLatRec.sin() * sinLatP) / (scLatRec.cos() * cosLatP);
1034 this.lonP = FastMath.atan2(sinLonP, cosLonP) + lon1;
1035
1036 }
1037
1038
1039 this.scLatP = FastMath.sinCos(latP);
1040
1041 final SinCos scLon = FastMath.sinCos(lon2 - lonP);
1042
1043 final double psi = greatCircleAngle(scLatSat, scLon);
1044 final SinCos scPsi = FastMath.sinCos(psi);
1045 if (FastMath.abs(FastMath.abs(latP) - 0.5 * FastMath.PI) < THRESHOLD) {
1046
1047 this.sinAzP = 0.0;
1048 if (latP < 0.0) {
1049 this.cosAzP = 1;
1050 } else {
1051 this.cosAzP = -1;
1052 }
1053 } else {
1054
1055 this.sinAzP = scLatSat.cos() * scLon.sin() / scPsi.sin();
1056 this.cosAzP = (scLatSat.sin() - scLatP.sin() * scPsi.cos()) / (scLatP.cos() * scPsi.sin());
1057 }
1058
1059
1060 this.s1 = FastMath.sqrt(r1 * r1 - rp * rp);
1061 this.s2 = FastMath.sqrt(r2 * r2 - rp * rp);
1062 }
1063
1064
1065
1066
1067
1068 public double getS1() {
1069 return s1;
1070 }
1071
1072
1073
1074
1075
1076 public double getS2() {
1077 return s2;
1078 }
1079
1080
1081
1082
1083
1084 public double getRadius() {
1085 return rp;
1086 }
1087
1088
1089
1090
1091
1092 public double getLatitude() {
1093 return latP;
1094 }
1095
1096
1097
1098
1099
1100 public double getLongitude() {
1101 return lonP;
1102 }
1103
1104
1105
1106
1107
1108 public double getSineAz() {
1109 return sinAzP;
1110 }
1111
1112
1113
1114
1115
1116 public double getCosineAz() {
1117 return cosAzP;
1118 }
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129 private double greatCircleAngle(final SinCos scLat, final SinCos scLon) {
1130 if (FastMath.abs(FastMath.abs(latP) - 0.5 * FastMath.PI) < THRESHOLD) {
1131 return FastMath.abs(FastMath.asin(scLat.sin()) - latP);
1132 } else {
1133 final double cosPhi = scLatP.sin() * scLat.sin() +
1134 scLatP.cos() * scLat.cos() * scLon.cos();
1135 final double sinPhi = FastMath.sqrt(1.0 - cosPhi * cosPhi);
1136 return FastMath.atan2(sinPhi, cosPhi);
1137 }
1138 }
1139 }
1140
1141
1142
1143
1144
1145 private static class FieldRay <T extends CalculusFieldElement<T>> {
1146
1147
1148 private static final double THRESHOLD = 1.0e-10;
1149
1150
1151 private final T s1;
1152
1153
1154 private final T s2;
1155
1156
1157 private final T rp;
1158
1159
1160 private final T latP;
1161
1162
1163 private final T lonP;
1164
1165
1166 private final FieldSinCos<T> scLatP;
1167
1168
1169 private final T sinAzP;
1170
1171
1172 private final T cosAzP;
1173
1174
1175
1176
1177
1178
1179
1180 FieldRay(final Field<T> field, final FieldGeodeticPoint<T> recP, final FieldGeodeticPoint<T> satP) {
1181
1182
1183 final T r1 = recP.getAltitude().add(RE);
1184 final T r2 = satP.getAltitude().add(RE);
1185
1186
1187 final T pi = r1.getPi();
1188 final T lat1 = recP.getLatitude();
1189 final T lat2 = satP.getLatitude();
1190 final T lon1 = recP.getLongitude();
1191 final T lon2 = satP.getLongitude();
1192 final FieldSinCos<T> scLatSat = FastMath.sinCos(lat2);
1193 final FieldSinCos<T> scLatRec = FastMath.sinCos(lat1);
1194
1195
1196 final T cosD = scLatRec.sin().multiply(scLatSat.sin()).
1197 add(scLatRec.cos().multiply(scLatSat.cos()).multiply(FastMath.cos(lon2.subtract(lon1))));
1198 final T sinD = FastMath.sqrt(cosD.multiply(cosD).negate().add(1.0));
1199 final T z = FastMath.atan2(sinD, cosD.subtract(r1.divide(r2)));
1200
1201
1202 this.rp = r1.multiply(FastMath.sin(z));
1203
1204
1205 if (FastMath.abs(FastMath.abs(lat1).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD) {
1206
1207
1208 if (lat1.getReal() < 0) {
1209 this.latP = z.negate();
1210 } else {
1211 this.latP = z;
1212 }
1213
1214
1215 if (z.getReal() < 0) {
1216 this.lonP = lon2;
1217 } else {
1218 this.lonP = lon2.add(pi);
1219 }
1220
1221 } else {
1222
1223
1224 final T deltaP = z.negate().add(pi.multiply(0.5));
1225 final FieldSinCos<T> scDeltaP = FastMath.sinCos(deltaP);
1226 final T sinAz = FastMath.sin(lon2.subtract(lon1)).multiply(scLatSat.cos()).divide(sinD);
1227 final T cosAz = scLatSat.sin().subtract(cosD.multiply(scLatRec.sin())).divide(sinD.multiply(scLatRec.cos()));
1228 final T sinLatP = scLatRec.sin().multiply(scDeltaP.cos()).subtract(scLatRec.cos().multiply(scDeltaP.sin()).multiply(cosAz));
1229 final T cosLatP = FastMath.sqrt(sinLatP.multiply(sinLatP).negate().add(1.0));
1230 this.latP = FastMath.atan2(sinLatP, cosLatP);
1231
1232
1233 final T sinLonP = sinAz.negate().multiply(scDeltaP.sin()).divide(cosLatP);
1234 final T cosLonP = scDeltaP.cos().subtract(scLatRec.sin().multiply(sinLatP)).divide(scLatRec.cos().multiply(cosLatP));
1235 this.lonP = FastMath.atan2(sinLonP, cosLonP).add(lon1);
1236
1237 }
1238
1239
1240 this.scLatP = FastMath.sinCos(latP);
1241
1242 final FieldSinCos<T> scLon = FastMath.sinCos(lon2.subtract(lonP));
1243
1244 final T psi = greatCircleAngle(scLatSat, scLon);
1245 final FieldSinCos<T> scPsi = FastMath.sinCos(psi);
1246 if (FastMath.abs(FastMath.abs(latP).subtract(pi.multiply(0.5)).getReal()) < THRESHOLD) {
1247
1248 this.sinAzP = field.getZero();
1249 if (latP.getReal() < 0.0) {
1250 this.cosAzP = field.getOne();
1251 } else {
1252 this.cosAzP = field.getOne().negate();
1253 }
1254 } else {
1255
1256 this.sinAzP = scLatSat.cos().multiply(scLon.sin()).divide(scPsi.sin());
1257 this.cosAzP = scLatSat.sin().subtract(scLatP.sin().multiply(scPsi.cos())).divide(scLatP.cos().multiply(scPsi.sin()));
1258 }
1259
1260
1261 this.s1 = FastMath.sqrt(r1.multiply(r1).subtract(rp.multiply(rp)));
1262 this.s2 = FastMath.sqrt(r2.multiply(r2).subtract(rp.multiply(rp)));
1263 }
1264
1265
1266
1267
1268
1269 public T getS1() {
1270 return s1;
1271 }
1272
1273
1274
1275
1276
1277 public T getS2() {
1278 return s2;
1279 }
1280
1281
1282
1283
1284
1285 public T getRadius() {
1286 return rp;
1287 }
1288
1289
1290
1291
1292
1293 public T getLatitude() {
1294 return latP;
1295 }
1296
1297
1298
1299
1300
1301 public T getLongitude() {
1302 return lonP;
1303 }
1304
1305
1306
1307
1308
1309 public T getSineAz() {
1310 return sinAzP;
1311 }
1312
1313
1314
1315
1316
1317 public T getCosineAz() {
1318 return cosAzP;
1319 }
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330 private T greatCircleAngle(final FieldSinCos<T> scLat, final FieldSinCos<T> scLon) {
1331 if (FastMath.abs(FastMath.abs(latP).getReal() - 0.5 * FastMath.PI) < THRESHOLD) {
1332 return FastMath.abs(FastMath.asin(scLat.sin()).subtract(latP));
1333 } else {
1334 final T cosPhi = scLatP.sin().multiply(scLat.sin()).add(
1335 scLatP.cos().multiply(scLat.cos()).multiply(scLon.cos()));
1336 final T sinPhi = FastMath.sqrt(cosPhi.multiply(cosPhi).negate().add(1.0));
1337 return FastMath.atan2(sinPhi, cosPhi);
1338 }
1339 }
1340 }
1341
1342
1343 private static class Segment {
1344
1345
1346 private final double[] latitudes;
1347
1348
1349 private final double[] longitudes;
1350
1351
1352 private final double[] heights;
1353
1354
1355 private final double deltaN;
1356
1357
1358
1359
1360
1361
1362 Segment(final int n, final Ray ray) {
1363
1364 final double s1 = ray.getS1();
1365 final double s2 = ray.getS2();
1366
1367
1368 this.deltaN = (s2 - s1) / n;
1369
1370
1371 final double[] s = getSegments(n, s1, s2);
1372
1373
1374 final double rp = ray.getRadius();
1375 final SinCos scLatP = FastMath.sinCos(ray.getLatitude());
1376
1377
1378 final int size = s.length;
1379 heights = new double[size];
1380 latitudes = new double[size];
1381 longitudes = new double[size];
1382 for (int i = 0; i < size; i++) {
1383
1384 heights[i] = FastMath.sqrt(s[i] * s[i] + rp * rp) - RE;
1385
1386
1387 final double tanDs = s[i] / rp;
1388 final double cosDs = 1.0 / FastMath.sqrt(1.0 + tanDs * tanDs);
1389 final double sinDs = tanDs * cosDs;
1390
1391
1392 final double sinLatS = scLatP.sin() * cosDs + scLatP.cos() * sinDs * ray.getCosineAz();
1393 final double cosLatS = FastMath.sqrt(1.0 - sinLatS * sinLatS);
1394 latitudes[i] = FastMath.atan2(sinLatS, cosLatS);
1395
1396
1397 final double sinLonS = sinDs * ray.getSineAz() * scLatP.cos();
1398 final double cosLonS = cosDs - scLatP.sin() * sinLatS;
1399 longitudes[i] = FastMath.atan2(sinLonS, cosLonS) + ray.getLongitude();
1400 }
1401 }
1402
1403
1404
1405
1406
1407
1408
1409
1410 private double[] getSegments(final int n, final double s1, final double s2) {
1411
1412 final double g = 0.5773502691896 * deltaN;
1413
1414 final double y = s1 + (deltaN - g) * 0.5;
1415 final double[] segments = new double[2 * n];
1416 int index = 0;
1417 for (int i = 0; i < n; i++) {
1418
1419 segments[index] = y + i * deltaN;
1420 index++;
1421 segments[index] = y + i * deltaN + g;
1422 index++;
1423 }
1424 return segments;
1425 }
1426
1427
1428
1429
1430
1431 public double[] getLatitudes() {
1432 return latitudes;
1433 }
1434
1435
1436
1437
1438
1439 public double[] getLongitudes() {
1440 return longitudes;
1441 }
1442
1443
1444
1445
1446
1447 public double[] getHeights() {
1448 return heights;
1449 }
1450
1451
1452
1453
1454
1455 public double getInterval() {
1456 return deltaN;
1457 }
1458 }
1459
1460
1461 private static class FieldSegment <T extends CalculusFieldElement<T>> {
1462
1463
1464 private final T[] latitudes;
1465
1466
1467 private final T[] longitudes;
1468
1469
1470 private final T[] heights;
1471
1472
1473 private final T deltaN;
1474
1475
1476
1477
1478
1479
1480
1481 FieldSegment(final Field<T> field, final int n, final FieldRay<T> ray) {
1482
1483 final T s1 = ray.getS1();
1484 final T s2 = ray.getS2();
1485
1486
1487 this.deltaN = s2.subtract(s1).divide(n);
1488
1489
1490 final T[] s = getSegments(field, n, s1, s2);
1491
1492
1493 final T rp = ray.getRadius();
1494 final FieldSinCos<T> scLatP = FastMath.sinCos(ray.getLatitude());
1495
1496
1497 final int size = s.length;
1498 heights = MathArrays.buildArray(field, size);
1499 latitudes = MathArrays.buildArray(field, size);
1500 longitudes = MathArrays.buildArray(field, size);
1501 for (int i = 0; i < size; i++) {
1502
1503 heights[i] = FastMath.sqrt(s[i].multiply(s[i]).add(rp.multiply(rp))).subtract(RE);
1504
1505
1506 final T tanDs = s[i].divide(rp);
1507 final T cosDs = FastMath.sqrt(tanDs.multiply(tanDs).add(1.0)).reciprocal();
1508 final T sinDs = tanDs.multiply(cosDs);
1509
1510
1511 final T sinLatS = scLatP.sin().multiply(cosDs).add(scLatP.cos().multiply(sinDs).multiply(ray.getCosineAz()));
1512 final T cosLatS = FastMath.sqrt(sinLatS.multiply(sinLatS).negate().add(1.0));
1513 latitudes[i] = FastMath.atan2(sinLatS, cosLatS);
1514
1515
1516 final T sinLonS = sinDs.multiply(ray.getSineAz()).multiply(scLatP.cos());
1517 final T cosLonS = cosDs.subtract(scLatP.sin().multiply(sinLatS));
1518 longitudes[i] = FastMath.atan2(sinLonS, cosLonS).add(ray.getLongitude());
1519 }
1520 }
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530 private T[] getSegments(final Field<T> field, final int n, final T s1, final T s2) {
1531
1532 final T g = deltaN.multiply(0.5773502691896);
1533
1534 final T y = s1.add(deltaN.subtract(g).multiply(0.5));
1535 final T[] segments = MathArrays.buildArray(field, 2 * n);
1536 int index = 0;
1537 for (int i = 0; i < n; i++) {
1538
1539 segments[index] = y.add(deltaN.multiply(i));
1540 index++;
1541 segments[index] = y.add(deltaN.multiply(i)).add(g);
1542 index++;
1543 }
1544 return segments;
1545 }
1546
1547
1548
1549
1550
1551 public T[] getLatitudes() {
1552 return latitudes;
1553 }
1554
1555
1556
1557
1558
1559 public T[] getLongitudes() {
1560 return longitudes;
1561 }
1562
1563
1564
1565
1566
1567 public T[] getHeights() {
1568 return heights;
1569 }
1570
1571
1572
1573
1574
1575 public T getInterval() {
1576 return deltaN;
1577 }
1578 }
1579
1580 }