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 UT1Scale estimatedUT1;
82
83
84 private final ParameterDriver primeMeridianOffsetDriver;
85
86
87 private final ParameterDriver primeMeridianDriftDriver;
88
89
90 private final ParameterDriver polarOffsetXDriver;
91
92
93 private final ParameterDriver polarDriftXDriver;
94
95
96 private final ParameterDriver polarOffsetYDriver;
97
98
99 private final 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 public FieldStaticTransform<Gradient> getStaticTransform(final FieldAbsoluteDate<Gradient> date,
354 final int freeParameters,
355 final Map<String, Integer> indices) {
356
357
358 final Gradient theta = linearModel(freeParameters, date, primeMeridianOffsetDriver, primeMeridianDriftDriver,
359 indices);
360 final Gradient thetaDot = primeMeridianDriftDriver.getValue(freeParameters, indices, date.toAbsoluteDate());
361
362
363 final Gradient xpNeg = linearModel(freeParameters, date,
364 polarOffsetXDriver, polarDriftXDriver, indices).negate();
365 final Gradient ypNeg = linearModel(freeParameters, date,
366 polarOffsetYDriver, polarDriftYDriver, indices).negate();
367 final Gradient xpNegDot = polarDriftXDriver.getValue(freeParameters, indices, date.toAbsoluteDate()).negate();
368 final Gradient ypNegDot = polarDriftYDriver.getValue(freeParameters, indices, date.toAbsoluteDate()).negate();
369
370 final Gradient zero = date.getField().getZero();
371 final FieldVector3D<Gradient> plusI = FieldVector3D.getPlusI(date.getField());
372 final FieldVector3D<Gradient> plusJ = FieldVector3D.getPlusJ(date.getField());
373 final FieldVector3D<Gradient> plusK = FieldVector3D.getPlusK(date.getField());
374
375
376 final FieldStaticTransform<Gradient> meridianShift =
377 FieldStaticTransform.of(date, new FieldVector3D<>(zero, zero, thetaDot),
378 new FieldRotation<>(plusK, theta, RotationConvention.FRAME_TRANSFORM));
379
380
381 final FieldStaticTransform<Gradient> poleShift =
382 FieldStaticTransform.compose(date,
383 FieldStaticTransform.of(date, new FieldVector3D<>(zero, xpNegDot, zero),
384 new FieldRotation<>(plusJ, xpNeg, RotationConvention.FRAME_TRANSFORM)),
385 FieldStaticTransform.of(date, new FieldVector3D<>(ypNegDot, zero, zero),
386 new FieldRotation<>(plusI, ypNeg, RotationConvention.FRAME_TRANSFORM)));
387
388 return FieldStaticTransform.compose(date, meridianShift, poleShift);
389
390 }
391
392
393
394
395
396
397
398
399
400
401
402
403 private <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date,
404 final T theta, final T thetaDot,
405 final T xpNeg, final T xpNegDot,
406 final T ypNeg, final T ypNegDot) {
407
408 final T zero = date.getField().getZero();
409 final FieldVector3D<T> plusI = FieldVector3D.getPlusI(date.getField());
410 final FieldVector3D<T> plusJ = FieldVector3D.getPlusJ(date.getField());
411 final FieldVector3D<T> plusK = FieldVector3D.getPlusK(date.getField());
412
413
414 final FieldTransform<T> meridianShift =
415 new FieldTransform<>(date,
416 new FieldRotation<>(plusK, theta, RotationConvention.FRAME_TRANSFORM),
417 new FieldVector3D<>(zero, zero, thetaDot));
418
419
420 final FieldTransform<T> poleShift =
421 new FieldTransform<>(date,
422 new FieldTransform<>(date,
423 new FieldRotation<>(plusJ, xpNeg, RotationConvention.FRAME_TRANSFORM),
424 new FieldVector3D<>(zero, xpNegDot, zero)),
425 new FieldTransform<>(date,
426 new FieldRotation<>(plusI, ypNeg, RotationConvention.FRAME_TRANSFORM),
427 new FieldVector3D<>(ypNegDot, zero, zero)));
428
429 return new FieldTransform<>(date, meridianShift, poleShift);
430
431 }
432
433
434
435
436
437
438
439 private double linearModel(final AbsoluteDate date,
440 final ParameterDriver offsetDriver, final ParameterDriver driftDriver) {
441 if (offsetDriver.getReferenceDate() == null) {
442 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
443 offsetDriver.getName());
444 }
445 final double dt = date.durationFrom(offsetDriver.getReferenceDate());
446 final double offset = offsetDriver.getValue();
447 final double drift = driftDriver.getValue();
448 return dt * drift + offset;
449 }
450
451
452
453
454
455
456
457
458 private <T extends CalculusFieldElement<T>> T linearModel(final FieldAbsoluteDate<T> date,
459 final ParameterDriver offsetDriver,
460 final ParameterDriver driftDriver) {
461 if (offsetDriver.getReferenceDate() == null) {
462 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
463 offsetDriver.getName());
464 }
465 final T dt = date.durationFrom(offsetDriver.getReferenceDate());
466 final double offset = offsetDriver.getValue();
467 final double drift = driftDriver.getValue();
468 return dt.multiply(drift).add(offset);
469 }
470
471
472
473
474
475
476
477
478
479
480 private Gradient linearModel(final int freeParameters, final FieldAbsoluteDate<Gradient> date,
481 final ParameterDriver offsetDriver, final ParameterDriver driftDriver,
482 final Map<String, Integer> indices) {
483 if (offsetDriver.getReferenceDate() == null) {
484 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
485 offsetDriver.getName());
486 }
487 final Gradient dt = date.durationFrom(offsetDriver.getReferenceDate());
488 final Gradient offset = offsetDriver.getValue(freeParameters, indices, date.toAbsoluteDate());
489 final Gradient drift = driftDriver.getValue(freeParameters, indices, date.toAbsoluteDate());
490 return dt.multiply(drift).add(offset);
491 }
492
493
494 private class EstimatedUT1Scale extends UT1Scale {
495
496
497
498 EstimatedUT1Scale() {
499 super(baseUT1.getEOPHistory(), baseUT1.getUTCScale());
500 }
501
502
503 @Override
504 public <T extends CalculusFieldElement<T>> T offsetFromTAI(final FieldAbsoluteDate<T> date) {
505 final T dut1 = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver).divide(EARTH_ANGULAR_VELOCITY);
506 return baseUT1.offsetFromTAI(date).add(dut1);
507 }
508
509
510 @Override
511 public TimeOffset offsetFromTAI(final AbsoluteDate date) {
512 final double dut1 = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver) / EARTH_ANGULAR_VELOCITY;
513 return baseUT1.offsetFromTAI(date).add(new TimeOffset(dut1));
514 }
515
516
517 @Override
518 public String getName() {
519 return baseUT1.getName() + "/estimated";
520 }
521
522 }
523 }