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.util.Collections;
20 import java.util.List;
21
22 import org.hipparchus.Field;
23 import org.hipparchus.CalculusFieldElement;
24 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.hipparchus.util.FastMath;
27 import org.hipparchus.util.FieldSinCos;
28 import org.hipparchus.util.MathUtils;
29 import org.hipparchus.util.SinCos;
30 import org.orekit.annotation.DefaultDataContext;
31 import org.orekit.bodies.FieldGeodeticPoint;
32 import org.orekit.bodies.GeodeticPoint;
33 import org.orekit.data.DataContext;
34 import org.orekit.frames.FieldStaticTransform;
35 import org.orekit.frames.StaticTransform;
36 import org.orekit.frames.TopocentricFrame;
37 import org.orekit.propagation.FieldSpacecraftState;
38 import org.orekit.propagation.SpacecraftState;
39 import org.orekit.time.AbsoluteDate;
40 import org.orekit.time.DateTimeComponents;
41 import org.orekit.time.FieldAbsoluteDate;
42 import org.orekit.time.TimeScale;
43 import org.orekit.utils.Constants;
44 import org.orekit.utils.ParameterDriver;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public class KlobucharIonoModel implements IonosphericModel, IonosphericDelayModel {
71
72
73 private final double[] alpha;
74
75
76 private final double[] beta;
77
78
79 private final TimeScale gps;
80
81
82
83
84
85
86
87
88
89
90 @DefaultDataContext
91 public KlobucharIonoModel(final double[] alpha, final double[] beta) {
92 this(alpha, beta, DataContext.getDefault().getTimeScales().getGPS());
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107 public KlobucharIonoModel(final double[] alpha,
108 final double[] beta,
109 final TimeScale gps) {
110 this.alpha = alpha.clone();
111 this.beta = beta.clone();
112 this.gps = gps;
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 public double pathDelay(final AbsoluteDate date, final GeodeticPoint geo,
130 final double elevation, final double azimuth, final double frequency,
131 final double[] parameters) {
132
133
134 final SinCos sc = FastMath.sinCos(azimuth);
135
136
137 final double rad2semi = 1. / FastMath.PI;
138 final double semi2rad = FastMath.PI;
139
140
141 final double psi = 0.0137 / (elevation / FastMath.PI + 0.11) - 0.022;
142
143
144
145 final double latIono = FastMath.min(
146 FastMath.max(geo.getLatitude() * rad2semi + psi * sc.cos(), -0.416),
147 0.416);
148
149
150
151 final double lonIono = geo.getLongitude() * rad2semi + (psi * sc.sin() / FastMath.cos(latIono * semi2rad));
152
153
154 final double latGeom = latIono + 0.064 * FastMath.cos((lonIono - 1.617) * semi2rad);
155
156
157
158 final DateTimeComponents dtc = date.getComponents(gps);
159 final int dofweek = dtc.getDate().getDayOfWeek();
160 final double secday = dtc.getTime().getSecondsInLocalDay();
161 final double tow = dofweek * 86400. + secday;
162
163 final double t = 43200. * lonIono + tow;
164 final double tsec = t - FastMath.floor(t / 86400.) * 86400;
165
166
167 final double slantFactor = 1.0 + 16.0 * FastMath.pow(0.53 - elevation / FastMath.PI, 3);
168
169
170 final double period = FastMath.max(72000., beta[0] + (beta[1] + (beta[2] + beta[3] * latGeom) * latGeom) * latGeom);
171
172
173
174 final double x = 2.0 * FastMath.PI * (tsec - 50400.0) / period;
175
176
177 final double amplitude = FastMath.max(0, alpha[0] + (alpha[1] + (alpha[2] + alpha[3] * latGeom) * latGeom) * latGeom);
178
179
180 double ionoTimeDelayL1 = slantFactor * (5. * 1e-9);
181 if (FastMath.abs(x) < 1.570) {
182 ionoTimeDelayL1 += slantFactor * (amplitude * (1.0 - FastMath.pow(x, 2) / 2.0 + FastMath.pow(x, 4) / 24.0));
183 }
184
185
186 final double ratio = FastMath.pow(1575.42e6 / frequency, 2);
187 return ratio * Constants.SPEED_OF_LIGHT * ionoTimeDelayL1;
188 }
189
190
191 @Override
192 public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame,
193 final double frequency, final double[] parameters) {
194 return pathDelay(state, baseFrame, state.getDate(), frequency, parameters);
195 }
196
197
198 @Override
199 public double pathDelay(final SpacecraftState state,
200 final TopocentricFrame baseFrame, final AbsoluteDate receptionDate,
201 final double frequency, final double[] parameters) {
202
203
204
205
206 final StaticTransform base2Inert = baseFrame.getStaticTransformTo(state.getFrame(), receptionDate);
207 final Vector3D position = base2Inert.getInverse().transformPosition(state.getPosition());
208
209
210 final double elevation = position.getDelta();
211
212
213 if (elevation > 0.0) {
214
215 final AbsoluteDate date = state.getDate();
216
217 final GeodeticPoint geo = baseFrame.getPoint();
218
219 double azimuth = FastMath.atan2(position.getX(), position.getY());
220 if (azimuth < 0.) {
221 azimuth += MathUtils.TWO_PI;
222 }
223
224 return pathDelay(date, geo, elevation, azimuth, frequency, parameters);
225 }
226
227 return 0.0;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldAbsoluteDate<T> date, final FieldGeodeticPoint<T> geo,
246 final T elevation, final T azimuth, final double frequency,
247 final T[] parameters) {
248
249
250 final FieldSinCos<T> sc = FastMath.sinCos(azimuth);
251
252
253 final Field<T> field = date.getField();
254 final T zero = field.getZero();
255 final T one = field.getOne();
256
257
258 final T pi = one.getPi();
259 final T rad2semi = pi.reciprocal();
260
261
262 final T psi = elevation.divide(pi).add(0.11).divide(0.0137).reciprocal().subtract(0.022);
263
264
265
266 final T latIono = FastMath.min(
267 FastMath.max(geo.getLatitude().multiply(rad2semi).add(psi.multiply(sc.cos())), zero.subtract(0.416)),
268 zero.newInstance(0.416));
269
270
271
272 final T lonIono = geo.getLongitude().multiply(rad2semi).add(psi.multiply(sc.sin()).divide(FastMath.cos(latIono.multiply(pi))));
273
274
275 final T latGeom = latIono.add(FastMath.cos(lonIono.subtract(1.617).multiply(pi)).multiply(0.064));
276
277
278
279 final DateTimeComponents dtc = date.getComponents(gps);
280 final int dofweek = dtc.getDate().getDayOfWeek();
281 final double secday = dtc.getTime().getSecondsInLocalDay();
282 final double tow = dofweek * 86400. + secday;
283
284 final T t = lonIono.multiply(43200.).add(tow);
285 final T tsec = t.subtract(FastMath.floor(t.divide(86400.)).multiply(86400.));
286
287
288 final T slantFactor = FastMath.pow(elevation.divide(pi).negate().add(0.53), 3).multiply(16.0).add(one);
289
290
291 final T period = FastMath.max(zero.newInstance(72000.), latGeom.multiply(latGeom.multiply(latGeom.multiply(beta[3]).add(beta[2])).add(beta[1])).add(beta[0]));
292
293
294
295 final T x = tsec.subtract(50400.0).multiply(pi.multiply(2.0)).divide(period);
296
297
298 final T amplitude = FastMath.max(zero, latGeom.multiply(latGeom.multiply(latGeom.multiply(alpha[3]).add(alpha[2])).add(alpha[1])).add(alpha[0]));
299
300
301 T ionoTimeDelayL1 = slantFactor.multiply(5. * 1e-9);
302 if (FastMath.abs(x.getReal()) < 1.570) {
303 ionoTimeDelayL1 = ionoTimeDelayL1.add(slantFactor.multiply(amplitude.multiply(one.subtract(FastMath.pow(x, 2).multiply(0.5)).add(FastMath.pow(x, 4).divide(24.0)))));
304 }
305
306
307 final double ratio = FastMath.pow(1575.42e6 / frequency, 2);
308 return ionoTimeDelayL1.multiply(Constants.SPEED_OF_LIGHT).multiply(ratio);
309 }
310
311
312 @Override
313 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state, final TopocentricFrame baseFrame,
314 final double frequency, final T[] parameters) {
315 return pathDelay(state, baseFrame, state.getDate(), frequency, parameters);
316 }
317
318
319 @Override
320 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state,
321 final TopocentricFrame baseFrame, final FieldAbsoluteDate<T> receptionDate,
322 final double frequency, final T[] parameters) {
323
324
325
326
327 final FieldStaticTransform<T> base2Inert = baseFrame.getStaticTransformTo(state.getFrame(), receptionDate);
328 final FieldVector3D<T> position = base2Inert.getInverse().transformPosition(state.getPosition());
329
330
331 final T elevation = position.getDelta();
332
333 if (elevation.getReal() > 0.0) {
334
335 final FieldAbsoluteDate<T> date = state.getDate();
336
337 final FieldGeodeticPoint<T> geo = baseFrame.getPoint(date.getField());
338
339 T azimuth = FastMath.atan2(position.getX(), position.getY());
340 if (azimuth.getReal() < 0.) {
341 azimuth = azimuth.add(MathUtils.TWO_PI);
342 }
343
344 return pathDelay(date, geo, elevation, azimuth, frequency, parameters);
345 }
346
347 return elevation.getField().getZero();
348 }
349
350 @Override
351 public List<ParameterDriver> getParametersDrivers() {
352 return Collections.emptyList();
353 }
354 }