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 org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.util.FastMath;
21 import org.hipparchus.util.MathArrays;
22 import org.orekit.annotation.DefaultDataContext;
23 import org.orekit.bodies.FieldGeodeticPoint;
24 import org.orekit.bodies.GeodeticPoint;
25 import org.orekit.data.DataContext;
26 import org.orekit.data.DataSource;
27 import org.orekit.errors.OrekitException;
28 import org.orekit.errors.OrekitMessages;
29 import org.orekit.time.DateTimeComponents;
30 import org.orekit.time.TimeScale;
31
32
33
34
35
36 public class NeQuickGalileo extends NeQuickModel {
37
38
39 private static final int N_START = 8;
40
41
42 private final double[] alpha;
43
44
45
46
47
48
49
50
51
52 @DefaultDataContext
53 public NeQuickGalileo(final double[] alpha) {
54 this(alpha, DataContext.getDefault().getTimeScales().getUTC());
55 }
56
57
58
59
60
61
62
63
64
65
66
67 public NeQuickGalileo(final double[] alpha, final TimeScale utc) {
68 super(utc);
69 this.alpha = alpha.clone();
70 }
71
72
73
74
75 public double[] getAlpha() {
76 return alpha.clone();
77 }
78
79
80
81
82
83
84
85 private double effectiveIonizationLevel(final double modip) {
86
87 if (alpha[0] == 0.0 && alpha[1] == 0.0 && alpha[2] == 0.0) {
88 return 63.7;
89 } else {
90
91 return FastMath.min(FastMath.max(alpha[0] + modip * (alpha[1] + modip * alpha[2]), 0.0), 400.0);
92 }
93 }
94
95
96
97
98
99
100
101
102 private <T extends CalculusFieldElement<T>> T effectiveIonizationLevel(final T modip) {
103
104 if (alpha[0] == 0.0 && alpha[1] == 0.0 && alpha[2] == 0.0) {
105 return modip.newInstance(63.7);
106 } else {
107
108 return FastMath.min(FastMath.max(modip.multiply(alpha[2]).add(alpha[1]).multiply(modip).add(alpha[0]),
109 0.0),
110 400.0);
111 }
112 }
113
114
115 @Override
116 double stec(final DateTimeComponents dateTime, final Ray ray) {
117
118
119 final double h1 = ray.getRecH();
120 final double tolerance;
121 if (h1 < 1000000.0) {
122 tolerance = 0.001;
123 } else {
124 tolerance = 0.01;
125 }
126
127
128 int n = N_START;
129 final Segment seg1 = new Segment(n, ray, ray.getS1(), ray.getS2());
130 double gn1 = stecIntegration(dateTime, seg1);
131 n *= 2;
132 final Segment seg2 = new Segment(n, ray, ray.getS1(), ray.getS2());
133 double gn2 = stecIntegration(dateTime, seg2);
134
135 int count = 1;
136 while (FastMath.abs(gn2 - gn1) > tolerance * FastMath.abs(gn1) && count < 20) {
137 gn1 = gn2;
138 n *= 2;
139 final Segment seg = new Segment(n, ray, ray.getS1(), ray.getS2());
140 gn2 = stecIntegration(dateTime, seg);
141 count += 1;
142 }
143
144
145 if (count == 20) {
146 throw new OrekitException(OrekitMessages.STEC_INTEGRATION_DID_NOT_CONVERGE);
147 }
148
149
150 return (gn2 + ((gn2 - gn1) / 15.0)) * 1.0e-16;
151
152 }
153
154
155 @Override
156 <T extends CalculusFieldElement<T>> T stec(final DateTimeComponents dateTime, final FieldRay<T> ray) {
157
158
159 final T h1 = ray.getRecH();
160 final double tolerance;
161 if (h1.getReal() < 1000000.0) {
162 tolerance = 0.001;
163 } else {
164 tolerance = 0.01;
165 }
166
167
168 int n = N_START;
169 final FieldSegment<T> seg1 = new FieldSegment<>(n, ray, ray.getS1(), ray.getS2());
170 T gn1 = stecIntegration(dateTime, seg1);
171 n *= 2;
172 final FieldSegment<T> seg2 = new FieldSegment<>(n, ray, ray.getS1(), ray.getS2());
173 T gn2 = stecIntegration(dateTime, seg2);
174
175 int count = 1;
176 while (FastMath.abs(gn2.subtract(gn1)).getReal() > FastMath.abs(gn1).multiply(tolerance).getReal() &&
177 count < 20) {
178 gn1 = gn2;
179 n *= 2;
180 final FieldSegment<T> seg = new FieldSegment<>(n, ray, ray.getS1(), ray.getS2());
181 gn2 = stecIntegration(dateTime, seg);
182 count += 1;
183 }
184
185
186 if (count == 20) {
187 throw new OrekitException(OrekitMessages.STEC_INTEGRATION_DID_NOT_CONVERGE);
188 }
189
190
191 return gn2.add(gn2.subtract(gn1).divide(15.0)).multiply(1.0e-16);
192
193 }
194
195
196
197
198
199
200
201
202 private double stecIntegration(final DateTimeComponents dateTime, final Segment seg) {
203
204
205 double density = 0.0;
206 for (int i = 0; i < seg.getNbPoints(); i++) {
207 final GeodeticPoint gp = seg.getPoint(i);
208 final double modip = GalileoHolder.INSTANCE.computeMODIP(gp.getLatitude(), gp.getLongitude());
209 final double az = effectiveIonizationLevel(modip);
210 density += electronDensity(dateTime, az, gp.getLatitude(), gp.getLongitude(), gp.getAltitude());
211 }
212
213 return 0.5 * seg.getInterval() * density;
214 }
215
216
217
218
219
220
221
222
223
224 private <T extends CalculusFieldElement<T>> T stecIntegration(final DateTimeComponents dateTime,
225 final FieldSegment<T> seg) {
226
227 T density = seg.getInterval().getField().getZero();
228 for (int i = 0; i < seg.getNbPoints(); i++) {
229 final FieldGeodeticPoint<T> gp = seg.getPoint(i);
230 final T modip = GalileoHolder.INSTANCE.computeMODIP(gp.getLatitude(), gp.getLongitude());
231 final T az = effectiveIonizationLevel(modip);
232 density = density.add(electronDensity(dateTime, az,
233 gp.getLatitude(), gp.getLongitude(), gp.getAltitude()));
234 }
235
236 return seg.getInterval().multiply(density).multiply(0.5);
237 }
238
239
240 @Override
241 protected double computeMODIP(final double latitude, final double longitude) {
242 return GalileoHolder.INSTANCE.computeMODIP(latitude, longitude);
243 }
244
245
246 @Override
247 protected <T extends CalculusFieldElement<T>> T computeMODIP(final T latitude, final T longitude) {
248 return GalileoHolder.INSTANCE.computeMODIP(latitude, longitude);
249 }
250
251
252 @Override
253 boolean applyChapmanParameters(final double hInKm) {
254 return hInKm < 100.0;
255 }
256
257
258 @Override
259 double[] computeExponentialArguments(final double h, final NeQuickParameters parameters) {
260
261
262
263 final double clippedH = FastMath.max(h, 100.0);
264
265
266 final double exp = clipExp(10.0 / (1.0 + FastMath.abs(clippedH - parameters.getHmF2())));
267 final double[] arguments = new double[3];
268 arguments[0] = (clippedH - parameters.getHmF2()) / parameters.getB2Bot();
269 arguments[1] = ((clippedH - parameters.getHmF1()) / parameters.getBF1(h)) * exp;
270 arguments[2] = ((clippedH - parameters.getHmE()) / parameters.getBE(h)) * exp;
271 return arguments;
272
273 }
274
275
276 @Override
277 <T extends CalculusFieldElement<T>> T[] computeExponentialArguments(final T h,
278 final FieldNeQuickParameters<T> parameters) {
279
280
281 final T clippedH = FastMath.max(h, 100);
282
283
284 final T exp = clipExp(FastMath.abs(clippedH.subtract(parameters.getHmF2())).add(1.0).reciprocal().multiply(10.0));
285 final T[] arguments = MathArrays.buildArray(h.getField(), 3);
286 arguments[0] = clippedH.subtract(parameters.getHmF2()).divide(parameters.getB2Bot());
287 arguments[1] = clippedH.subtract(parameters.getHmF1()).divide(parameters.getBF1(h)).multiply(exp);
288 arguments[2] = clippedH.subtract(parameters.getHmE()).divide(parameters.getBE(h)).multiply(exp);
289 return arguments;
290
291 }
292
293
294 @Override
295 double computeH0(final NeQuickParameters parameters) {
296
297 final int month = parameters.getDateTime().getDate().getMonth();
298
299
300 final double ka;
301 if (month > 3 && month < 10) {
302
303 ka = 6.705 - 0.014 * parameters.getAzr() - 0.008 * parameters.getHmF2();
304 } else {
305
306 final double ratio = parameters.getHmF2() / parameters.getB2Bot();
307 ka = -7.77 + 0.097 * ratio * ratio + 0.153 * parameters.getNmF2();
308 }
309
310
311 double kb = parameters.join(ka, 2.0, 1.0, ka - 2.0);
312 kb = parameters.join(8.0, kb, 1.0, kb - 8.0);
313
314
315 final double hA = kb * parameters.getB2Bot();
316
317
318 final double x = 0.01 * (hA - 150.0);
319 final double v = (0.041163 * x - 0.183981) * x + 1.424472;
320
321
322 return hA / v;
323
324 }
325
326
327 @Override
328 <T extends CalculusFieldElement<T>> T computeH0(final FieldNeQuickParameters<T> parameters) {
329
330 final int month = parameters.getDateTime().getDate().getMonth();
331
332
333 final T one = parameters.getAzr().getField().getOne();
334
335
336 final T ka;
337 if (month > 3 && month < 10) {
338
339 ka = parameters.getAzr().multiply(0.014).add(parameters.getHmF2().multiply(0.008)).negate().add(6.705);
340 } else {
341
342 final T ratio = parameters.getHmF2().divide(parameters.getB2Bot());
343 ka = ratio.multiply(ratio).multiply(0.097).add(parameters.getNmF2().multiply(0.153)).add(-7.77);
344 }
345
346
347 T kb = parameters.join(ka, one.newInstance(2.0), one, ka.subtract(2.0));
348 kb = parameters.join(one.newInstance(8.0), kb, one, kb.subtract(8.0));
349
350
351 final T hA = kb.multiply(parameters.getB2Bot());
352
353
354 final T x = hA.subtract(150.0).multiply(0.01);
355 final T v = x.multiply(0.041163).subtract(0.183981).multiply(x).add(1.424472);
356
357
358 return hA.divide(v);
359
360 }
361
362
363
364
365
366
367
368
369 private static class GalileoHolder {
370
371
372 private static final String MODIP_GRID = "/assets/org/orekit/nequick/modipNeQG_wrapped.asc";
373
374
375 private static final ModipGrid INSTANCE =
376 new ModipGrid(36, 36,
377 new DataSource(MODIP_GRID, () -> GalileoHolder.class.getResourceAsStream(MODIP_GRID)),
378 true);
379
380
381
382
383
384
385 private GalileoHolder() {
386 }
387
388 }
389
390 }