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