1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.models.earth;
18
19 import java.io.Serializable;
20 import java.util.Arrays;
21
22 import org.apache.commons.math3.analysis.BivariateFunction;
23 import org.apache.commons.math3.analysis.UnivariateFunction;
24 import org.apache.commons.math3.analysis.interpolation.LinearInterpolator;
25 import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
26 import org.apache.commons.math3.exception.DimensionMismatchException;
27 import org.apache.commons.math3.exception.InsufficientDataException;
28 import org.apache.commons.math3.exception.NoDataException;
29 import org.apache.commons.math3.exception.NonMonotonicSequenceException;
30 import org.apache.commons.math3.exception.OutOfRangeException;
31 import org.apache.commons.math3.util.FastMath;
32 import org.apache.commons.math3.util.MathArrays;
33 import org.orekit.data.DataProvidersManager;
34 import org.orekit.errors.OrekitException;
35 import org.orekit.errors.OrekitMessages;
36 import org.orekit.utils.Constants;
37 import org.orekit.utils.InterpolationTableLoader;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class SaastamoinenModel implements TroposphericDelayModel {
62
63
64 public static final String DELTA_R_FILE_NAME = "^saastamoinen-correction\\.txt$";
65
66
67 private static final long serialVersionUID = 20160126L;
68
69
70 private static final double[] X_VALUES_FOR_B = {
71 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0
72 };
73
74
75 private static final double[] Y_VALUES_FOR_B = {
76 1.156, 1.079, 1.006, 0.938, 0.874, 0.813, 0.757, 0.654, 0.563
77 };
78
79
80 private static final double[] E_COEFFICIENTS = {
81 -37.2465, 0.213166, -0.000256908
82 };
83
84
85 private final UnivariateFunction bFunction;
86
87
88 private final PolynomialFunction eFunction;
89
90
91 private final BilinearInterpolatingFunction deltaRFunction;
92
93
94 private double t0;
95
96
97 private double p0;
98
99
100 private double r0;
101
102
103
104
105
106
107
108
109
110 @Deprecated
111 public SaastamoinenModel(final double t0, final double p0, final double r0)
112 throws OrekitException {
113 this(t0, p0, r0, DELTA_R_FILE_NAME);
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127 public SaastamoinenModel(final double t0, final double p0, final double r0,
128 final String deltaRFileName)
129 throws OrekitException {
130 this(t0, p0, r0,
131 deltaRFileName == null ? defaultDeltaR() : loadDeltaR(deltaRFileName));
132 }
133
134
135
136
137
138
139
140
141 private SaastamoinenModel(final double t0, final double p0, final double r0,
142 final BilinearInterpolatingFunction deltaR) {
143 this.t0 = t0;
144 this.p0 = p0;
145 this.r0 = r0;
146 this.bFunction = new LinearInterpolator().interpolate(X_VALUES_FOR_B, Y_VALUES_FOR_B);
147 this.eFunction = new PolynomialFunction(E_COEFFICIENTS);
148 this.deltaRFunction = deltaR;
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162 public static SaastamoinenModel getStandardModel()
163 throws OrekitException {
164 return new SaastamoinenModel(273.16 + 18, 1013.25, 0.5, DELTA_R_FILE_NAME);
165 }
166
167
168 public double calculatePathDelay(final double elevation, final double height) {
169
170 final double T = t0 - 6.5e-3 * height;
171
172 final double P = p0 * FastMath.pow(1.0 - 2.26e-5 * height, 5.225);
173
174 final double R = r0 * FastMath.exp(-6.396e-4 * height);
175
176
177 final double B = bFunction.value(height / 1e3);
178
179 final double e = R * FastMath.exp(eFunction.value(T));
180
181
182 final double zInDegree = FastMath.abs(90.0 - elevation);
183 final double z = FastMath.toRadians(zInDegree);
184
185
186 final double deltaR = getDeltaR(height, zInDegree);
187
188
189 final double tan = FastMath.tan(z);
190 final double delta = 2.277e-3 / FastMath.cos(z) *
191 (P + (1255d / T + 5e-2) * e - B * tan * tan) + deltaR;
192
193 return delta;
194 }
195
196
197 public double calculateSignalDelay(final double elevation, final double height) {
198 return calculatePathDelay(elevation, height) / Constants.SPEED_OF_LIGHT;
199 }
200
201
202
203
204
205
206 private double getDeltaR(final double height, final double zenith) {
207
208 final double h = FastMath.min(Math.max(0, height), 5000);
209
210
211 final double z = FastMath.min(Math.abs(zenith), 90);
212 return deltaRFunction.value(h, z);
213 }
214
215
216
217
218
219
220
221 private static BilinearInterpolatingFunction loadDeltaR(final String deltaRFileName)
222 throws OrekitException {
223
224
225 final InterpolationTableLoader loader = new InterpolationTableLoader();
226 DataProvidersManager.getInstance().feed(deltaRFileName, loader);
227 if (!loader.stillAcceptsData()) {
228 return new BilinearInterpolatingFunction(loader.getAbscissaGrid(),
229 loader.getOrdinateGrid(),
230 loader.getValuesSamples());
231 }
232 throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE,
233 deltaRFileName.replaceAll("^\\^", "").replaceAll("\\$$", ""));
234 }
235
236
237
238
239 private static BilinearInterpolatingFunction defaultDeltaR() {
240
241
242
243
244
245
246 final double xValForR[] = {
247 0, 500, 1000, 1500, 2000, 3000, 4000, 5000
248 };
249
250
251 final double yValForR[] = {
252 0.0, 60.0, 66.0, 70.0, 73.0, 75.0, 76.0, 77.0, 78.0, 78.50, 79.0, 79.50, 79.75, 80.0, 90.0
253 };
254
255 final double[][] fval = new double[][] {
256 {
257 0.000, 0.003, 0.006, 0.012, 0.020, 0.031, 0.039, 0.050, 0.065, 0.075, 0.087, 0.102, 0.111, 0.121, 0.121
258 }, {
259 0.000, 0.003, 0.006, 0.011, 0.018, 0.028, 0.035, 0.045, 0.059, 0.068, 0.079, 0.093, 0.101, 0.110, 0.110
260 }, {
261 0.000, 0.002, 0.005, 0.010, 0.017, 0.025, 0.032, 0.041, 0.054, 0.062, 0.072, 0.085, 0.092, 0.100, 0.100
262 }, {
263 0.000, 0.002, 0.005, 0.009, 0.015, 0.023, 0.029, 0.037, 0.049, 0.056, 0.065, 0.077, 0.083, 0.091, 0.091
264 }, {
265 0.000, 0.002, 0.004, 0.008, 0.013, 0.021, 0.026, 0.033, 0.044, 0.051, 0.059, 0.070, 0.076, 0.083, 0.083
266 }, {
267 0.000, 0.002, 0.003, 0.006, 0.011, 0.017, 0.021, 0.027, 0.036, 0.042, 0.049, 0.058, 0.063, 0.068, 0.068
268 }, {
269 0.000, 0.001, 0.003, 0.005, 0.009, 0.014, 0.017, 0.022, 0.030, 0.034, 0.040, 0.047, 0.052, 0.056, 0.056
270 }, {
271 0.000, 0.001, 0.002, 0.004, 0.007, 0.011, 0.014, 0.018, 0.024, 0.028, 0.033, 0.039, 0.043, 0.047, 0.047
272 }
273 };
274
275
276 return new BilinearInterpolatingFunction(xValForR, yValForR, fval);
277
278 }
279
280
281
282
283 private Object writeReplace() {
284 return new DataTransferObject(this);
285 }
286
287
288 private static class DataTransferObject implements Serializable {
289
290
291 private static final long serialVersionUID = 20160126L;
292
293
294 private double t0;
295
296
297 private double p0;
298
299
300 private double r0;
301
302
303 private final double[] xval;
304
305
306 private final double[] yval;
307
308
309 private final double[][] fval;
310
311
312
313
314 DataTransferObject(final SaastamoinenModel model) {
315 this.t0 = model.t0;
316 this.p0 = model.p0;
317 this.r0 = model.r0;
318 this.xval = model.deltaRFunction.xval.clone();
319 this.yval = model.deltaRFunction.yval.clone();
320 this.fval = model.deltaRFunction.fval.clone();
321 }
322
323
324
325
326 private Object readResolve() {
327 return new SaastamoinenModel(t0, p0, r0,
328 new BilinearInterpolatingFunction(xval, yval, fval));
329 }
330
331 }
332
333
334
335
336
337
338
339
340
341 private static class BilinearInterpolatingFunction implements BivariateFunction {
342
343
344
345
346
347 private static final int MIN_NUM_POINTS = 2;
348
349
350 private final double[] xval;
351
352
353 private final double[] yval;
354
355
356 private final double[][] fval;
357
358
359
360
361
362
363
364
365
366
367
368
369
370 BilinearInterpolatingFunction(final double[] x, final double[] y, final double[][] f)
371 throws DimensionMismatchException, IllegalArgumentException, NoDataException,
372 NonMonotonicSequenceException {
373
374 if (x == null || y == null || f == null || f[0] == null) {
375 throw new IllegalArgumentException("All arguments must be non-null");
376 }
377
378 final int xLen = x.length;
379 final int yLen = y.length;
380
381 if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
382 throw new NoDataException();
383 }
384
385 if (xLen < MIN_NUM_POINTS || yLen < MIN_NUM_POINTS || f.length < MIN_NUM_POINTS ||
386 f[0].length < MIN_NUM_POINTS) {
387 throw new InsufficientDataException();
388 }
389
390 if (xLen != f.length) {
391 throw new DimensionMismatchException(xLen, f.length);
392 }
393
394 if (yLen != f[0].length) {
395 throw new DimensionMismatchException(yLen, f[0].length);
396 }
397
398 MathArrays.checkOrder(x);
399 MathArrays.checkOrder(y);
400
401 xval = x.clone();
402 yval = y.clone();
403 fval = f.clone();
404 }
405
406 @Override
407 public double value(final double x, final double y) {
408 final int offset = 1;
409 final int count = offset + 1;
410 final int i = searchIndex(x, xval, offset, count);
411 final int j = searchIndex(y, yval, offset, count);
412
413 final double x1 = xval[i];
414 final double x2 = xval[i + 1];
415 final double y1 = yval[j];
416 final double y2 = yval[j + 1];
417 final double fQ11 = fval[i][j];
418 final double fQ21 = fval[i + 1][j];
419 final double fQ12 = fval[i][j + 1];
420 final double fQ22 = fval[i + 1][j + 1];
421
422 final double f = (fQ11 * (x2 - x) * (y2 - y) +
423 fQ21 * (x - x1) * (y2 - y) +
424 fQ12 * (x2 - x) * (y - y1) +
425 fQ22 * (x - x1) * (y - y1)) /
426 ((x2 - x1) * (y2 - y1));
427
428 return f;
429 }
430
431
432
433
434
435
436
437
438
439
440
441
442
443 private int searchIndex(final double c, final double[] val, final int offset, final int count) {
444 int r = Arrays.binarySearch(val, c);
445
446 if (r == -1 || r == -val.length - 1) {
447 throw new OutOfRangeException(c, val[0], val[val.length - 1]);
448 }
449
450 if (r < 0) {
451
452
453
454
455 r = -r - offset - 1;
456 } else {
457 r -= offset;
458 }
459
460 if (r < 0) {
461 r = 0;
462 }
463
464 if ((r + count) >= val.length) {
465
466
467 r = val.length - count;
468 }
469
470 return r;
471 }
472
473 }
474
475 }
476