1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.gnss.attitude;
18
19 import org.hipparchus.Field;
20 import org.hipparchus.CalculusFieldElement;
21 import org.hipparchus.analysis.UnivariateFunction;
22 import org.hipparchus.analysis.differentiation.FDSFactory;
23 import org.hipparchus.analysis.differentiation.FieldDerivativeStructure;
24 import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
25 import org.hipparchus.analysis.solvers.UnivariateSolverUtils;
26 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27 import org.hipparchus.geometry.euclidean.threed.Vector3D;
28 import org.hipparchus.util.FastMath;
29 import org.hipparchus.util.FieldSinCos;
30 import org.hipparchus.util.SinCos;
31 import org.orekit.frames.FieldTransform;
32 import org.orekit.frames.Frame;
33 import org.orekit.frames.LOFType;
34 import org.orekit.time.AbsoluteDate;
35 import org.orekit.time.FieldAbsoluteDate;
36 import org.orekit.time.FieldTimeStamped;
37 import org.orekit.utils.ExtendedPVCoordinatesProvider;
38 import org.orekit.utils.FieldPVCoordinates;
39 import org.orekit.utils.FieldPVCoordinatesProvider;
40 import org.orekit.utils.PVCoordinates;
41 import org.orekit.utils.TimeStampedFieldAngularCoordinates;
42 import org.orekit.utils.TimeStampedFieldPVCoordinates;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 class GNSSFieldAttitudeContext<T extends CalculusFieldElement<T>> implements FieldTimeStamped<T> {
59
60
61 private static final int ORDER = 2;
62
63
64 private static final PVCoordinates PLUS_Y_PV =
65 new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);
66
67
68 private static final PVCoordinates MINUS_Z_PV =
69 new PVCoordinates(Vector3D.MINUS_K, Vector3D.ZERO, Vector3D.ZERO);
70
71
72 private static final double BETA_SIGN_CHANGE_PROTECTION = FastMath.toRadians(0.07);
73
74
75 private final FDSFactory<T> factory;
76
77
78 private final FieldPVCoordinates<T> plusY;
79
80
81 private final FieldPVCoordinates<T> minusZ;
82
83
84 private final AbsoluteDate dateDouble;
85
86
87 private final FieldAbsoluteDate<T> date;
88
89
90 private final FieldAbsoluteDate<FieldDerivativeStructure<T>> dateDS;
91
92
93 private final ExtendedPVCoordinatesProvider sun;
94
95
96 private final FieldPVCoordinatesProvider<T> pvProv;
97
98
99 private final Frame inertialFrame;
100
101
102 private final FieldDerivativeStructure<T> svbCos;
103
104
105 private final boolean morning;
106
107
108 private final FieldDerivativeStructure<T> delta;
109
110
111 private T muRate;
112
113
114 private double cNight;
115
116
117 private double cNoon;
118
119
120 private FieldTurnSpan<T> turnSpan;
121
122
123
124
125
126
127
128
129
130 GNSSFieldAttitudeContext(final FieldAbsoluteDate<T> date,
131 final ExtendedPVCoordinatesProvider sun, final FieldPVCoordinatesProvider<T> pvProv,
132 final Frame inertialFrame, final FieldTurnSpan<T> turnSpan) {
133
134 final Field<T> field = date.getField();
135 factory = new FDSFactory<>(field, 1, ORDER);
136 plusY = new FieldPVCoordinates<>(field, PLUS_Y_PV);
137 minusZ = new FieldPVCoordinates<>(field, MINUS_Z_PV);
138
139 this.dateDouble = date.toAbsoluteDate();
140 this.date = date;
141 this.dateDS = addDerivatives(date);
142 this.sun = sun;
143 this.pvProv = pvProv;
144 this.inertialFrame = inertialFrame;
145 final FieldPVCoordinates<FieldDerivativeStructure<T>> sunPVDS = sun.getPVCoordinates(dateDS, inertialFrame);
146
147 final TimeStampedFieldPVCoordinates<T> svPV = pvProv.getPVCoordinates(date, inertialFrame);
148 final FieldPVCoordinates<FieldDerivativeStructure<T>> svPVDS =
149 svPV.toDerivativeStructurePV(factory.getCompiler().getOrder());
150 this.svbCos = FieldVector3D.dotProduct(sunPVDS.getPosition(), svPVDS.getPosition()).
151 divide(sunPVDS.getPosition().getNorm().multiply(svPVDS.getPosition().getNorm()));
152 this.morning = Vector3D.dotProduct(svPV.getVelocity().toVector3D(), sunPVDS.getPosition().toVector3D()) >= 0.0;
153
154 this.muRate = svPV.getAngularVelocity().getNorm();
155
156 this.turnSpan = turnSpan;
157
158 final FieldDerivativeStructure<T> absDelta;
159 if (svbCos.getValue().getReal() <= 0) {
160
161 absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos().negate().add(svbCos.getPi()));
162 } else {
163
164 absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos());
165 }
166 delta = absDelta.copySign(absDelta.getPartialDerivative(1).negate());
167
168 }
169
170
171
172
173
174 private FieldAbsoluteDate<T> removeDerivatives(final FieldAbsoluteDate<FieldDerivativeStructure<T>> d) {
175 final AbsoluteDate dd = d.toAbsoluteDate();
176 final FieldDerivativeStructure<T> offset = d.durationFrom(dd);
177 return new FieldAbsoluteDate<>(date.getField(), dd).shiftedBy(offset.getValue());
178 }
179
180
181
182
183
184 private FieldAbsoluteDate<FieldDerivativeStructure<T>> addDerivatives(final FieldAbsoluteDate<T> d) {
185 final AbsoluteDate dd = d.toAbsoluteDate();
186 final T offset = d.durationFrom(dd);
187 return new FieldAbsoluteDate<>(factory.getDerivativeField(), dd).shiftedBy(factory.variable(0, offset));
188 }
189
190
191
192
193
194 public TimeStampedFieldAngularCoordinates<T> nominalYaw(final FieldAbsoluteDate<T> d) {
195 final TimeStampedFieldPVCoordinates<T> svPV = pvProv.getPVCoordinates(d, inertialFrame);
196 return new TimeStampedFieldAngularCoordinates<>(d,
197 svPV.normalize(),
198 sun.getPVCoordinates(d, inertialFrame).crossProduct(svPV).normalize(),
199 minusZ,
200 plusY,
201 1.0e-9);
202 }
203
204
205
206
207
208 public T beta(final FieldAbsoluteDate<T> d) {
209 final TimeStampedFieldPVCoordinates<T> svPV = pvProv.getPVCoordinates(d, inertialFrame);
210 return FieldVector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPV.getMomentum()).
211 negate().
212 add(svPV.getPosition().getX().getPi().multiply(0.5));
213 }
214
215
216
217
218
219 private FieldDerivativeStructure<T> betaDS(final FieldAbsoluteDate<FieldDerivativeStructure<T>> d) {
220 final FieldPVCoordinates<FieldDerivativeStructure<T>> svPV =
221 pvProv.getPVCoordinates(removeDerivatives(d), inertialFrame).
222 toDerivativeStructurePV(d.getField().getZero().getOrder());
223 return FieldVector3D.angle(sun.getPVCoordinates(d, inertialFrame).getPosition(), svPV.getMomentum()).
224 negate().
225 add(svPV.getPosition().getX().getPi().multiply(0.5));
226 }
227
228
229
230
231 public FieldDerivativeStructure<T> betaDS() {
232 return betaDS(dateDS);
233 }
234
235
236 @Override
237 public FieldAbsoluteDate<T> getDate() {
238 return date;
239 }
240
241
242
243
244 public FieldTurnSpan<T> getTurnSpan() {
245 return turnSpan;
246 }
247
248
249
250
251 public T getSVBcos() {
252 return svbCos.getValue();
253 }
254
255
256
257
258
259
260
261
262
263
264
265 public T getSecuredBeta() {
266 final T beta = beta(getDate());
267 return FastMath.abs(beta.getReal()) < BETA_SIGN_CHANGE_PROTECTION ?
268 beta(turnSpan.getTurnStartDate()) :
269 beta;
270 }
271
272
273
274
275
276
277 public boolean linearModelStillActive(final T linearPhi, final T phiDot) {
278 final AbsoluteDate absDate = date.toAbsoluteDate();
279 final double dt0 = turnSpan.getTurnEndDate().durationFrom(date).getReal();
280 final UnivariateFunction yawReached = dt -> {
281 final AbsoluteDate t = absDate.shiftedBy(dt);
282 final Vector3D pSun = sun.getPVCoordinates(t, inertialFrame).getPosition();
283 final PVCoordinates pv = pvProv.getPVCoordinates(date.shiftedBy(dt), inertialFrame).toPVCoordinates();
284 final Vector3D pSat = pv.getPosition();
285 final Vector3D targetX = Vector3D.crossProduct(pSat, Vector3D.crossProduct(pSun, pSat)).normalize();
286
287 final double phi = linearPhi.getReal() + dt * phiDot.getReal();
288 final SinCos sc = FastMath.sinCos(phi);
289 final Vector3D pU = pv.getPosition().normalize();
290 final Vector3D mU = pv.getMomentum().normalize();
291 final Vector3D omega = new Vector3D(-phiDot.getReal(), pU);
292 final Vector3D currentX = new Vector3D(-sc.sin(), mU, -sc.cos(), Vector3D.crossProduct(pU, mU));
293 final Vector3D currentXDot = Vector3D.crossProduct(omega, currentX);
294
295 return Vector3D.dotProduct(targetX, currentXDot);
296 };
297 final double fullTurn = 2 * FastMath.PI / FastMath.abs(phiDot.getReal());
298 final double dtMin = FastMath.min(turnSpan.getTurnStartDate().durationFrom(date).getReal(), dt0 - 60.0);
299 final double dtMax = FastMath.max(dtMin + fullTurn, dt0 + 60.0);
300 double[] bracket = UnivariateSolverUtils.bracket(yawReached, dt0,
301 dtMin, dtMax, fullTurn / 100, 1.0, 100);
302 if (yawReached.value(bracket[0]) <= 0.0) {
303
304 bracket = UnivariateSolverUtils.bracket(yawReached, 0.5 * (bracket[0] + bracket[1] + fullTurn),
305 bracket[1], bracket[1] + fullTurn, fullTurn / 100, 1.0, 100);
306 }
307 final double dt = new BracketingNthOrderBrentSolver(1.0e-3, 5).
308 solve(100, yawReached, bracket[0], bracket[1]);
309 turnSpan.updateEnd(date.shiftedBy(dt), absDate);
310
311 return dt > 0.0;
312
313 }
314
315
316
317
318
319
320 public boolean setUpTurnRegion(final double cosNight, final double cosNoon) {
321 this.cNight = cosNight;
322 this.cNoon = cosNoon;
323
324 if (svbCos.getValue().getReal() < cNight || svbCos.getValue().getReal() > cNoon) {
325
326 return true;
327 } else {
328
329
330 return inTurnTimeRange();
331 }
332
333 }
334
335
336
337
338 public FieldDerivativeStructure<T> getDeltaDS() {
339 return delta;
340 }
341
342
343
344
345 public T getOrbitAngleSinceMidnight() {
346 final T absAngle = inOrbitPlaneAbsoluteAngle(FastMath.acos(svbCos.getValue()).negate().add(svbCos.getValue().getPi()));
347 return morning ? absAngle : absAngle.negate();
348 }
349
350
351
352
353 public boolean inSunSide() {
354 return svbCos.getValue().getReal() > 0;
355 }
356
357
358
359
360
361
362
363 public T getYawStart(final T sunBeta) {
364 final T halfSpan = turnSpan.getTurnDuration().multiply(muRate).multiply(0.5);
365 return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue()));
366 }
367
368
369
370
371
372
373
374 public T getYawEnd(final T sunBeta) {
375 final T halfSpan = turnSpan.getTurnDuration().multiply(muRate).multiply(0.5);
376 return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue().negate()));
377 }
378
379
380
381
382
383
384
385 public T yawRate(final T sunBeta) {
386 return getYawEnd(sunBeta).subtract(getYawStart(sunBeta)).divide(turnSpan.getTurnDuration());
387 }
388
389
390
391
392 public T getMuRate() {
393 return muRate;
394 }
395
396
397
398
399
400
401
402
403
404
405 private FieldDerivativeStructure<T> inOrbitPlaneAbsoluteAngle(final FieldDerivativeStructure<T> angle) {
406 return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta(getDate()))));
407 }
408
409
410
411
412
413
414
415
416
417
418
419 public T inOrbitPlaneAbsoluteAngle(final T angle) {
420 return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta(getDate()))));
421 }
422
423
424
425
426
427
428
429
430
431 public T computePhi(final T sunBeta, final T inOrbitPlaneAngle) {
432 return FastMath.atan2(FastMath.tan(sunBeta).negate(), FastMath.sin(inOrbitPlaneAngle));
433 }
434
435
436
437
438
439 public void setHalfSpan(final T halfSpan, final double endMargin) {
440 final FieldAbsoluteDate<T> start = date.shiftedBy(delta.getValue().subtract(halfSpan).divide(muRate));
441 final FieldAbsoluteDate<T> end = date.shiftedBy(delta.getValue().add(halfSpan).divide(muRate));
442 final AbsoluteDate estimationDate = getDate().toAbsoluteDate();
443 if (turnSpan == null) {
444 turnSpan = new FieldTurnSpan<>(start, end, estimationDate, endMargin);
445 } else {
446 turnSpan.updateStart(start, estimationDate);
447 turnSpan.updateEnd(end, estimationDate);
448 }
449 }
450
451
452
453
454 public boolean inTurnTimeRange() {
455 return turnSpan != null && turnSpan.inTurnTimeRange(dateDouble);
456 }
457
458
459
460
461 public T timeSinceTurnStart() {
462 return getDate().durationFrom(turnSpan.getTurnStartDate());
463 }
464
465
466
467
468
469
470 public TimeStampedFieldAngularCoordinates<T> turnCorrectedAttitude(final T yaw, final T yawDot) {
471 return turnCorrectedAttitude(factory.build(yaw, yawDot, yaw.getField().getZero()));
472 }
473
474
475
476
477
478 public TimeStampedFieldAngularCoordinates<T> turnCorrectedAttitude(final FieldDerivativeStructure<T> yaw) {
479
480
481 final FieldPVCoordinates<T> svPV = pvProv.getPVCoordinates(date, inertialFrame);
482 final FieldVector3D<T> p = svPV.getPosition();
483 final FieldVector3D<T> v = svPV.getVelocity();
484 final FieldVector3D<T> a = svPV.getAcceleration();
485 final T r2 = p.getNormSq();
486 final T r = FastMath.sqrt(r2);
487 final FieldVector3D<T> keplerianJerk = new FieldVector3D<>(FieldVector3D.dotProduct(p, v).multiply(-3).divide(r2), a,
488 a.getNorm().negate().divide(r), v);
489 final FieldPVCoordinates<T> velocity = new FieldPVCoordinates<>(v, a, keplerianJerk);
490 final FieldPVCoordinates<T> momentum = svPV.crossProduct(velocity);
491
492 final FieldSinCos<FieldDerivativeStructure<T>> sc = FastMath.sinCos(yaw);
493 final FieldDerivativeStructure<T> c = sc.cos().negate();
494 final FieldDerivativeStructure<T> s = sc.sin().negate();
495 final T z = yaw.getFactory().getValueField().getZero();
496 final FieldVector3D<T> m0 = new FieldVector3D<>(s.getValue(), c.getValue(), z);
497 final FieldVector3D<T> m1 = new FieldVector3D<>(s.getPartialDerivative(1), c.getPartialDerivative(1), z);
498 final FieldVector3D<T> m2 = new FieldVector3D<>(s.getPartialDerivative(2), c.getPartialDerivative(2), z);
499 return new TimeStampedFieldAngularCoordinates<>(date,
500 svPV.normalize(), momentum.normalize(),
501 minusZ, new FieldPVCoordinates<>(m0, m1, m2),
502 1.0e-9);
503
504 }
505
506
507
508
509 public TimeStampedFieldAngularCoordinates<T> orbitNormalYaw() {
510 final FieldTransform<T> t = LOFType.LVLH_CCSDS.transformFromInertial(date, pvProv.getPVCoordinates(date, inertialFrame));
511 return new TimeStampedFieldAngularCoordinates<>(date,
512 t.getRotation(),
513 t.getRotationRate(),
514 t.getRotationAcceleration());
515 }
516
517 }