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.bodies.FieldGeodeticPoint;
23 import org.orekit.bodies.GeodeticPoint;
24 import org.orekit.data.DataSource;
25 import org.orekit.time.DateTimeComponents;
26 import org.orekit.time.TimeScale;
27 import org.orekit.utils.units.Unit;
28
29
30
31
32
33
34
35
36
37
38 public class NeQuickItu extends NeQuickModel {
39
40
41 private static final double H_1000 = 1000000.0;
42
43
44 private static final double H_2000 = 2000000.0;
45
46
47 private static final double H0 = 90.0;
48
49
50 private static final double HD = 5.0;
51
52
53 private static final int N_START = 8;
54
55
56 private static final int N_STOP = 1024;
57
58
59 private static final double EPS_SMALL = 1.0e-3;
60
61
62 private static final double EPS_MEDIUM = 1.0e-2;
63
64
65 private final double f107;
66
67
68
69
70
71 public NeQuickItu(final double f107, final TimeScale utc) {
72 super(utc);
73 this.f107 = f107;
74 }
75
76
77
78
79 public double getF107() {
80 return f107;
81 }
82
83
84 @Override
85 double stec(final DateTimeComponents dateTime, final Ray ray) {
86 if (ray.getSatH() <= H_2000) {
87 if (ray.getRecH() >= H_1000) {
88
89 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), ray.getS2());
90 } else {
91
92 final double h1000 = NeQuickModel.RE + H_1000;
93 final double s1000 = FastMath.sqrt(h1000 * h1000 - ray.getRadius() * ray.getRadius());
94 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), s1000) +
95 stecIntegration(dateTime, EPS_MEDIUM, ray, s1000, ray.getS2());
96 }
97 } else {
98 if (ray.getRecH() >= H_2000) {
99
100 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), ray.getS2());
101 } else {
102 final double h2000 = NeQuickModel.RE + H_2000;
103 final double s2000 = FastMath.sqrt(h2000 * h2000 - ray.getRadius() * ray.getRadius());
104 if (ray.getRecH() >= H_1000) {
105
106 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), s2000) +
107 stecIntegration(dateTime, EPS_SMALL, ray, s2000, ray.getS2());
108 } else {
109
110 final double h1000 = NeQuickModel.RE + H_1000;
111 final double s1000 = FastMath.sqrt(h1000 * h1000 - ray.getRadius() * ray.getRadius());
112 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), s1000) +
113 stecIntegration(dateTime, EPS_MEDIUM, ray, s1000, s2000) +
114 stecIntegration(dateTime, EPS_MEDIUM, ray, s2000, ray.getS2());
115 }
116 }
117 }
118 }
119
120
121 @Override
122 <T extends CalculusFieldElement<T>> T stec(final DateTimeComponents dateTime, final FieldRay<T> ray) {
123 if (ray.getSatH().getReal() <= H_2000) {
124 if (ray.getRecH().getReal() >= H_1000) {
125
126 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), ray.getS2());
127 } else {
128
129 final double h1000 = NeQuickModel.RE + H_1000;
130 final T s1000 = FastMath.sqrt(ray.getRadius().multiply(ray.getRadius()).negate().add(h1000 * h1000));
131 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), s1000).
132 add(stecIntegration(dateTime, EPS_MEDIUM, ray, s1000, ray.getS2()));
133 }
134 } else {
135 if (ray.getRecH().getReal() >= H_2000) {
136
137 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), ray.getS2());
138 } else {
139 final double h2000 = NeQuickModel.RE + H_2000;
140 final T s2000 = FastMath.sqrt(ray.getRadius().multiply(ray.getRadius()).negate().add(h2000 * h2000));
141 if (ray.getRecH().getReal() >= H_1000) {
142
143 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), s2000).
144 add(stecIntegration(dateTime, EPS_SMALL, ray, s2000, ray.getS2()));
145 } else {
146
147 final double h1000 = NeQuickModel.RE + H_1000;
148 final T s1000 = FastMath.sqrt(ray.getRadius().multiply(ray.getRadius()).negate().add(h1000 * h1000));
149 return stecIntegration(dateTime, EPS_SMALL, ray, ray.getS1(), s1000).
150 add(stecIntegration(dateTime, EPS_MEDIUM, ray, s1000, s2000)).
151 add(stecIntegration(dateTime, EPS_MEDIUM, ray, s2000, ray.getS2()));
152 }
153 }
154 }
155 }
156
157
158
159
160
161
162
163
164
165
166
167 private double stecIntegration(final DateTimeComponents dateTime, final double eps, final Ray ray, final double s1,
168 final double s2) {
169
170 final FourierTimeSeries fourierTimeSeries = computeFourierTimeSeries(dateTime, f107);
171 double gInt1 = Double.NaN;
172 double gInt2 = Double.NaN;
173
174 for (int n = N_START; n <= N_STOP; n = 2 * n) {
175
176
177 final Segment segment = new Segment(n, ray, s1, s2);
178 double sum = 0;
179 for (int i = 0; i < segment.getNbPoints(); ++i) {
180 final GeodeticPoint gp = segment.getPoint(i);
181 final double ed = electronDensity(fourierTimeSeries,
182 gp.getLatitude(), gp.getLongitude(), gp.getAltitude());
183 sum += ed;
184 }
185
186 gInt1 = gInt2;
187 gInt2 = sum * 0.5 * segment.getInterval();
188 if (FastMath.abs(gInt1 - gInt2) <= FastMath.abs(gInt1 * eps)) {
189
190 break;
191 }
192
193 }
194
195 return Unit.TOTAL_ELECTRON_CONTENT_UNIT.fromSI(gInt2 + (gInt2 - gInt1) / 15.0);
196
197 }
198
199
200
201
202
203
204
205
206
207
208
209
210 private <T extends CalculusFieldElement<T>> T stecIntegration(final DateTimeComponents dateTime,
211 final double eps,
212 final FieldRay<T> ray, final T s1, final T s2) {
213
214 final FieldFourierTimeSeries<T> fourierTimeSeries = computeFourierTimeSeries(dateTime, s1.newInstance(f107));
215 T gInt1 = s1.newInstance(Double.NaN);
216 T gInt2 = s1.newInstance(Double.NaN);
217
218 for (int n = N_START; n <= N_STOP; n = 2 * n) {
219
220
221 final FieldSegment<T> segment = new FieldSegment<>(n, ray, s1, s2);
222 T sum = s1.getField().getZero();
223 for (int i = 0; i < segment.getNbPoints(); ++i) {
224 final FieldGeodeticPoint<T> gp = segment.getPoint(i);
225 final T ed = electronDensity(fourierTimeSeries, gp.getLatitude(), gp.getLongitude(), gp.getAltitude());
226 sum = sum.add(ed);
227 }
228
229 gInt1 = gInt2;
230 gInt2 = sum.multiply(0.5).multiply(segment.getInterval());
231 if (FastMath.abs(gInt1.subtract(gInt2).getReal()) <= FastMath.abs(gInt1.getReal() * eps)) {
232
233 break;
234 }
235
236 }
237
238 return Unit.TOTAL_ELECTRON_CONTENT_UNIT.fromSI(gInt2.add(gInt2.subtract(gInt1).divide(15.0)));
239
240 }
241
242
243 @Override
244 protected double computeMODIP(final double latitude, final double longitude) {
245 return ItuHolder.INSTANCE.computeMODIP(latitude, longitude);
246 }
247
248
249 @Override
250 protected <T extends CalculusFieldElement<T>> T computeMODIP(final T latitude, final T longitude) {
251 return ItuHolder.INSTANCE.computeMODIP(latitude, longitude);
252 }
253
254
255 @Override
256 boolean applyChapmanParameters(final double hInKm) {
257 return false;
258 }
259
260
261 @Override
262 double[] computeExponentialArguments(final double h, final NeQuickParameters parameters) {
263 final double exp = clipExp(10.0 / (1.0 + FastMath.abs(h - parameters.getHmF2())));
264 final double[] arguments = new double[3];
265 arguments[0] = fixLowHArg( (h - parameters.getHmF2()) / parameters.getB2Bot(), h);
266 arguments[1] = fixLowHArg(((h - parameters.getHmF1()) / parameters.getBF1(h)) * exp, h);
267 arguments[2] = fixLowHArg(((h - parameters.getHmE()) / parameters.getBE(h)) * exp, h);
268 return arguments;
269 }
270
271
272 @Override
273 <T extends CalculusFieldElement<T>> T[] computeExponentialArguments(final T h,
274 final FieldNeQuickParameters<T> parameters) {
275 final T exp = clipExp(FastMath.abs(h.subtract(parameters.getHmF2())).add(1.0).reciprocal().multiply(10.0));
276 final T[] arguments = MathArrays.buildArray(h.getField(), 3);
277 arguments[0] = fixLowHArg(h.subtract(parameters.getHmF2()).divide(parameters.getB2Bot()), h);
278 arguments[1] = fixLowHArg(h.subtract(parameters.getHmF1()).divide(parameters.getBF1(h)).multiply(exp), h);
279 arguments[2] = fixLowHArg(h.subtract(parameters.getHmE()).divide(parameters.getBE(h)).multiply(exp), h);
280 return arguments;
281 }
282
283
284
285
286
287
288
289
290 private double fixLowHArg(final double arg, final double h) {
291 return h < H0 ? arg * (HD + H0 - h) / HD : arg;
292 }
293
294
295
296
297
298
299
300
301
302 private <T extends CalculusFieldElement<T>> T fixLowHArg(final T arg, final T h) {
303 return h.getReal() < H0 ? arg.multiply(h.negate().add(HD + H0)).divide(HD) : arg;
304 }
305
306
307 @Override
308 double computeH0(final NeQuickParameters parameters) {
309 final double b2k = -0.0538 * parameters.getFoF2() -
310 0.00664 * parameters.getHmF2() +
311 0.113 * parameters.getHmF2() / parameters.getB2Bot() +
312 0.00257 * parameters.getAzr() +
313 3.22;
314 return parameters.getB2Bot() * parameters.join(b2k, 1.0, 2.0, b2k - 1.0);
315 }
316
317
318 @Override
319 <T extends CalculusFieldElement<T>> T computeH0(final FieldNeQuickParameters<T> parameters) {
320 final T b2k = parameters.getFoF2().multiply(-0.0538).
321 subtract(parameters.getHmF2().multiply(0.00664)).
322 add(parameters.getHmF2().multiply(0.113).divide(parameters.getB2Bot())).
323 add(parameters.getAzr().multiply(0.00257)).
324 add (3.22);
325 return parameters.getB2Bot().
326 multiply(parameters.join(b2k, b2k.newInstance(1.0), b2k.newInstance(2.0), b2k.subtract(1.0)));
327 }
328
329
330
331
332
333
334
335
336 private static class ItuHolder {
337
338
339 private static final String MODIP_GRID = "/assets/org/orekit/nequick/modip.asc";
340
341
342 private static final ModipGrid INSTANCE =
343 new ModipGrid(180, 180,
344 new DataSource(MODIP_GRID, () -> ItuHolder.class.getResourceAsStream(MODIP_GRID)),
345 false);
346
347
348
349
350
351
352 private ItuHolder() {
353 }
354
355 }
356
357 }