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