1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.estimation.measurements;
18
19 import java.util.Map;
20
21 import org.hipparchus.CalculusFieldElement;
22 import org.hipparchus.analysis.differentiation.Gradient;
23 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
24 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25 import org.hipparchus.geometry.euclidean.threed.Rotation;
26 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
27 import org.hipparchus.geometry.euclidean.threed.Vector3D;
28 import org.hipparchus.util.FastMath;
29 import org.orekit.errors.OrekitException;
30 import org.orekit.errors.OrekitMessages;
31 import org.orekit.frames.FieldStaticTransform;
32 import org.orekit.frames.FieldTransform;
33 import org.orekit.frames.StaticTransform;
34 import org.orekit.frames.Transform;
35 import org.orekit.frames.TransformProvider;
36 import org.orekit.time.AbsoluteDate;
37 import org.orekit.time.FieldAbsoluteDate;
38 import org.orekit.time.TimeOffset;
39 import org.orekit.time.UT1Scale;
40 import org.orekit.utils.IERSConventions;
41 import org.orekit.utils.ParameterDriver;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class EstimatedEarthFrameProvider implements TransformProvider {
65
66
67 public static final double EARTH_ANGULAR_VELOCITY = 7.292115146706979e-5;
68
69
70
71
72
73
74
75 private static final double ANGULAR_SCALE = FastMath.scalb(1.0, -22);
76
77
78 private final UT1Scale baseUT1;
79
80
81 private final transient UT1Scale estimatedUT1;
82
83
84 private final transient ParameterDriver primeMeridianOffsetDriver;
85
86
87 private final transient ParameterDriver primeMeridianDriftDriver;
88
89
90 private final transient ParameterDriver polarOffsetXDriver;
91
92
93 private final transient ParameterDriver polarDriftXDriver;
94
95
96 private final transient ParameterDriver polarOffsetYDriver;
97
98
99 private final transient ParameterDriver polarDriftYDriver;
100
101
102
103
104
105
106
107
108
109
110
111 public EstimatedEarthFrameProvider(final UT1Scale baseUT1) {
112
113 this.primeMeridianOffsetDriver = new ParameterDriver("prime-meridian-offset",
114 0.0, ANGULAR_SCALE,
115 -FastMath.PI, FastMath.PI);
116
117 this.primeMeridianDriftDriver = new ParameterDriver("prime-meridian-drift",
118 0.0, ANGULAR_SCALE,
119 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
120
121 this.polarOffsetXDriver = new ParameterDriver("polar-offset-X",
122 0.0, ANGULAR_SCALE,
123 -FastMath.PI, FastMath.PI);
124
125 this.polarDriftXDriver = new ParameterDriver("polar-drift-X",
126 0.0, ANGULAR_SCALE,
127 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
128
129 this.polarOffsetYDriver = new ParameterDriver("polar-offset-Y",
130 0.0, ANGULAR_SCALE,
131 -FastMath.PI, FastMath.PI);
132
133 this.polarDriftYDriver = new ParameterDriver("polar-drift-Y",
134 0.0, ANGULAR_SCALE,
135 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
136
137 this.baseUT1 = baseUT1;
138 this.estimatedUT1 = new EstimatedUT1Scale();
139
140 }
141
142
143
144
145
146
147
148
149
150 public ParameterDriver getPrimeMeridianOffsetDriver() {
151 return primeMeridianOffsetDriver;
152 }
153
154
155
156
157
158
159
160
161
162 public ParameterDriver getPrimeMeridianDriftDriver() {
163 return primeMeridianDriftDriver;
164 }
165
166
167
168
169
170
171
172 public ParameterDriver getPolarOffsetXDriver() {
173 return polarOffsetXDriver;
174 }
175
176
177
178
179
180
181
182 public ParameterDriver getPolarDriftXDriver() {
183 return polarDriftXDriver;
184 }
185
186
187
188
189
190
191
192 public ParameterDriver getPolarOffsetYDriver() {
193 return polarOffsetYDriver;
194 }
195
196
197
198
199
200
201
202 public ParameterDriver getPolarDriftYDriver() {
203 return polarDriftYDriver;
204 }
205
206
207
208
209 public UT1Scale getEstimatedUT1() {
210 return estimatedUT1;
211 }
212
213
214 @Override
215 public Transform getTransform(final AbsoluteDate date) {
216
217
218 final double theta = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver);
219 final double thetaDot = primeMeridianDriftDriver.getValue();
220 final Transform meridianShift =
221 new Transform(date,
222 new Rotation(Vector3D.PLUS_K, theta, RotationConvention.FRAME_TRANSFORM),
223 new Vector3D(0, 0, thetaDot));
224
225
226 final double xpNeg = -linearModel(date, polarOffsetXDriver, polarDriftXDriver);
227 final double ypNeg = -linearModel(date, polarOffsetYDriver, polarDriftYDriver);
228 final double xpNegDot = -polarDriftXDriver.getValue();
229 final double ypNegDot = -polarDriftYDriver.getValue();
230 final Transform poleShift =
231 new Transform(date,
232 new Transform(date,
233 new Rotation(Vector3D.PLUS_J, xpNeg, RotationConvention.FRAME_TRANSFORM),
234 new Vector3D(0.0, xpNegDot, 0.0)),
235 new Transform(date,
236 new Rotation(Vector3D.PLUS_I, ypNeg, RotationConvention.FRAME_TRANSFORM),
237 new Vector3D(ypNegDot, 0.0, 0.0)));
238
239 return new Transform(date, meridianShift, poleShift);
240
241 }
242
243
244 @Override
245 public StaticTransform getStaticTransform(final AbsoluteDate date) {
246
247
248 final double theta = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver);
249 final StaticTransform meridianShift = StaticTransform.of(
250 date,
251 new Rotation(Vector3D.PLUS_K, theta, RotationConvention.FRAME_TRANSFORM)
252 );
253
254
255 final double xpNeg = -linearModel(date, polarOffsetXDriver, polarDriftXDriver);
256 final double ypNeg = -linearModel(date, polarOffsetYDriver, polarDriftYDriver);
257 final StaticTransform poleShift = StaticTransform.compose(
258 date,
259 StaticTransform.of(
260 date,
261 new Rotation(Vector3D.PLUS_J, xpNeg, RotationConvention.FRAME_TRANSFORM)),
262 StaticTransform.of(
263 date,
264 new Rotation(Vector3D.PLUS_I, ypNeg, RotationConvention.FRAME_TRANSFORM)));
265
266 return StaticTransform.compose(date, meridianShift, poleShift);
267
268 }
269
270
271 @Override
272 public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
273
274 final T zero = date.getField().getZero();
275
276
277 final T theta = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver);
278 final T thetaDot = zero.newInstance(primeMeridianDriftDriver.getValue());
279
280
281 final T xpNeg = linearModel(date, polarOffsetXDriver, polarDriftXDriver).negate();
282 final T ypNeg = linearModel(date, polarOffsetYDriver, polarDriftYDriver).negate();
283 final T xpNegDot = zero.subtract(polarDriftXDriver.getValue());
284 final T ypNegDot = zero.subtract(polarDriftYDriver.getValue());
285
286 return getTransform(date, theta, thetaDot, xpNeg, xpNegDot, ypNeg, ypNegDot);
287
288 }
289
290
291 @Override
292 public <T extends CalculusFieldElement<T>> FieldStaticTransform<T> getStaticTransform(final FieldAbsoluteDate<T> date) {
293
294
295 final T theta = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver);
296 final FieldStaticTransform<T> meridianShift = FieldStaticTransform.of(
297 date,
298 new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), theta, RotationConvention.FRAME_TRANSFORM)
299 );
300
301
302 final T xpNeg = linearModel(date, polarOffsetXDriver, polarDriftXDriver).negate();
303 final T ypNeg = linearModel(date, polarOffsetYDriver, polarDriftYDriver).negate();
304 final FieldStaticTransform<T> poleShift = FieldStaticTransform.compose(
305 date,
306 FieldStaticTransform.of(
307 date,
308 new FieldRotation<>(FieldVector3D.getPlusJ(date.getField()), xpNeg, RotationConvention.FRAME_TRANSFORM)),
309 FieldStaticTransform.of(
310 date,
311 new FieldRotation<>(FieldVector3D.getPlusI(date.getField()), ypNeg, RotationConvention.FRAME_TRANSFORM)));
312
313 return FieldStaticTransform.compose(date, meridianShift, poleShift);
314
315 }
316
317
318
319
320
321
322
323
324 public FieldTransform<Gradient> getTransform(final FieldAbsoluteDate<Gradient> date,
325 final int freeParameters,
326 final Map<String, Integer> indices) {
327
328
329 final Gradient theta = linearModel(freeParameters, date,
330 primeMeridianOffsetDriver, primeMeridianDriftDriver,
331 indices);
332 final Gradient thetaDot = primeMeridianDriftDriver.getValue(freeParameters, indices, date.toAbsoluteDate());
333
334
335 final Gradient xpNeg = linearModel(freeParameters, date,
336 polarOffsetXDriver, polarDriftXDriver, indices).negate();
337 final Gradient ypNeg = linearModel(freeParameters, date,
338 polarOffsetYDriver, polarDriftYDriver, indices).negate();
339 final Gradient xpNegDot = polarDriftXDriver.getValue(freeParameters, indices, date.toAbsoluteDate()).negate();
340 final Gradient ypNegDot = polarDriftYDriver.getValue(freeParameters, indices, date.toAbsoluteDate()).negate();
341
342 return getTransform(date, theta, thetaDot, xpNeg, xpNegDot, ypNeg, ypNegDot);
343
344 }
345
346
347
348
349
350
351
352
353
354
355
356
357 private <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date,
358 final T theta, final T thetaDot,
359 final T xpNeg, final T xpNegDot,
360 final T ypNeg, final T ypNegDot) {
361
362 final T zero = date.getField().getZero();
363 final FieldVector3D<T> plusI = FieldVector3D.getPlusI(date.getField());
364 final FieldVector3D<T> plusJ = FieldVector3D.getPlusJ(date.getField());
365 final FieldVector3D<T> plusK = FieldVector3D.getPlusK(date.getField());
366
367
368 final FieldTransform<T> meridianShift =
369 new FieldTransform<>(date,
370 new FieldRotation<>(plusK, theta, RotationConvention.FRAME_TRANSFORM),
371 new FieldVector3D<>(zero, zero, thetaDot));
372
373
374 final FieldTransform<T> poleShift =
375 new FieldTransform<>(date,
376 new FieldTransform<>(date,
377 new FieldRotation<>(plusJ, xpNeg, RotationConvention.FRAME_TRANSFORM),
378 new FieldVector3D<>(zero, xpNegDot, zero)),
379 new FieldTransform<>(date,
380 new FieldRotation<>(plusI, ypNeg, RotationConvention.FRAME_TRANSFORM),
381 new FieldVector3D<>(ypNegDot, zero, zero)));
382
383 return new FieldTransform<>(date, meridianShift, poleShift);
384
385 }
386
387
388
389
390
391
392
393 private double linearModel(final AbsoluteDate date,
394 final ParameterDriver offsetDriver, final ParameterDriver driftDriver) {
395 if (offsetDriver.getReferenceDate() == null) {
396 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
397 offsetDriver.getName());
398 }
399 final double dt = date.durationFrom(offsetDriver.getReferenceDate());
400 final double offset = offsetDriver.getValue();
401 final double drift = driftDriver.getValue();
402 return dt * drift + offset;
403 }
404
405
406
407
408
409
410
411
412 private <T extends CalculusFieldElement<T>> T linearModel(final FieldAbsoluteDate<T> date,
413 final ParameterDriver offsetDriver,
414 final ParameterDriver driftDriver) {
415 if (offsetDriver.getReferenceDate() == null) {
416 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
417 offsetDriver.getName());
418 }
419 final T dt = date.durationFrom(offsetDriver.getReferenceDate());
420 final double offset = offsetDriver.getValue();
421 final double drift = driftDriver.getValue();
422 return dt.multiply(drift).add(offset);
423 }
424
425
426
427
428
429
430
431
432
433
434 private Gradient linearModel(final int freeParameters, final FieldAbsoluteDate<Gradient> date,
435 final ParameterDriver offsetDriver, final ParameterDriver driftDriver,
436 final Map<String, Integer> indices) {
437 if (offsetDriver.getReferenceDate() == null) {
438 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
439 offsetDriver.getName());
440 }
441 final Gradient dt = date.durationFrom(offsetDriver.getReferenceDate());
442 final Gradient offset = offsetDriver.getValue(freeParameters, indices, date.toAbsoluteDate());
443 final Gradient drift = driftDriver.getValue(freeParameters, indices, date.toAbsoluteDate());
444 return dt.multiply(drift).add(offset);
445 }
446
447
448 private class EstimatedUT1Scale extends UT1Scale {
449
450
451
452 EstimatedUT1Scale() {
453 super(baseUT1.getEOPHistory(), baseUT1.getUTCScale());
454 }
455
456
457 @Override
458 public <T extends CalculusFieldElement<T>> T offsetFromTAI(final FieldAbsoluteDate<T> date) {
459 final T dut1 = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver).divide(EARTH_ANGULAR_VELOCITY);
460 return baseUT1.offsetFromTAI(date).add(dut1);
461 }
462
463
464 @Override
465 public TimeOffset offsetFromTAI(final AbsoluteDate date) {
466 final double dut1 = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver) / EARTH_ANGULAR_VELOCITY;
467 return baseUT1.offsetFromTAI(date).add(new TimeOffset(dut1));
468 }
469
470
471 @Override
472 public String getName() {
473 return baseUT1.getName() + "/estimated";
474 }
475
476 }
477 }