1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.rugged.linesensor;
18
19 import org.apache.commons.math3.analysis.UnivariateFunction;
20 import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
21 import org.apache.commons.math3.analysis.solvers.BracketingNthOrderBrentSolver;
22 import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
23 import org.apache.commons.math3.exception.NoBracketingException;
24 import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
25 import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
26 import org.apache.commons.math3.linear.Array2DRowRealMatrix;
27 import org.apache.commons.math3.linear.ArrayRealVector;
28 import org.apache.commons.math3.linear.DecompositionSolver;
29 import org.apache.commons.math3.linear.MatrixUtils;
30 import org.apache.commons.math3.linear.QRDecomposition;
31 import org.apache.commons.math3.linear.RealMatrix;
32 import org.apache.commons.math3.linear.RealVector;
33 import org.apache.commons.math3.linear.SingularMatrixException;
34 import org.apache.commons.math3.linear.SingularValueDecomposition;
35 import org.apache.commons.math3.util.FastMath;
36 import org.apache.commons.math3.util.Precision;
37 import org.orekit.frames.Transform;
38 import org.orekit.rugged.errors.RuggedException;
39 import org.orekit.rugged.errors.RuggedExceptionWrapper;
40 import org.orekit.rugged.utils.SpacecraftToObservedBody;
41 import org.orekit.time.AbsoluteDate;
42 import org.orekit.utils.Constants;
43 import org.orekit.utils.PVCoordinates;
44
45
46
47
48
49
50
51 public class SensorMeanPlaneCrossing {
52
53
54 private static final int CACHED_RESULTS = 6;
55
56
57 private final SpacecraftToObservedBody scToBody;
58
59
60 private final double midLine;
61
62
63 private final Transform midBodyToInert;
64
65
66 private final Transform midScToInert;
67
68
69 private final int minLine;
70
71
72 private final int maxLine;
73
74
75 private final boolean lightTimeCorrection;
76
77
78 private final boolean aberrationOfLightCorrection;
79
80
81 private final LineSensor sensor;
82
83
84 private final Vector3D meanPlaneNormal;
85
86
87 private final int maxEval;
88
89
90 private final double accuracy;
91
92
93 private final CrossingResult[] cachedResults;
94
95
96
97
98
99
100
101
102
103
104
105
106 public SensorMeanPlaneCrossing(final LineSensor sensor,
107 final SpacecraftToObservedBody scToBody,
108 final int minLine, final int maxLine,
109 final boolean lightTimeCorrection,
110 final boolean aberrationOfLightCorrection,
111 final int maxEval, final double accuracy)
112 throws RuggedException {
113 this(sensor, scToBody, minLine, maxLine, lightTimeCorrection, aberrationOfLightCorrection,
114 maxEval, accuracy, computeMeanPlaneNormal(sensor, minLine, maxLine), new CrossingResult[0]);
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 public SensorMeanPlaneCrossing(final LineSensor sensor,
131 final SpacecraftToObservedBody scToBody,
132 final int minLine, final int maxLine,
133 final boolean lightTimeCorrection,
134 final boolean aberrationOfLightCorrection,
135 final int maxEval, final double accuracy,
136 final Vector3D meanPlaneNormal,
137 final CrossingResult[] cachedResults)
138 throws RuggedException {
139
140 this.sensor = sensor;
141 this.minLine = minLine;
142 this.maxLine = maxLine;
143 this.lightTimeCorrection = lightTimeCorrection;
144 this.aberrationOfLightCorrection = aberrationOfLightCorrection;
145 this.maxEval = maxEval;
146 this.accuracy = accuracy;
147 this.scToBody = scToBody;
148
149 this.midLine = 0.5 * (minLine + maxLine);
150 final AbsoluteDate midDate = sensor.getDate(midLine);
151 this.midBodyToInert = scToBody.getBodyToInertial(midDate);
152 this.midScToInert = scToBody.getScToInertial(midDate);
153
154 this.meanPlaneNormal = meanPlaneNormal;
155
156 this.cachedResults = new CrossingResult[CACHED_RESULTS];
157 System.arraycopy(cachedResults, 0, this.cachedResults, 0, cachedResults.length);
158
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172 private static Vector3D computeMeanPlaneNormal(final LineSensor sensor, final int minLine, final int maxLine)
173 throws RuggedException {
174
175 final AbsoluteDate midDate = sensor.getDate(0.5 * (minLine + maxLine));
176
177
178
179
180 final RealMatrix matrix = MatrixUtils.createRealMatrix(3, 2 * sensor.getNbPixels());
181 for (int i = 0; i < sensor.getNbPixels(); ++i) {
182 final Vector3D l = sensor.getLos(midDate, i);
183 matrix.setEntry(0, 2 * i, l.getX());
184 matrix.setEntry(1, 2 * i, l.getY());
185 matrix.setEntry(2, 2 * i, l.getZ());
186 matrix.setEntry(0, 2 * i + 1, -l.getX());
187 matrix.setEntry(1, 2 * i + 1, -l.getY());
188 matrix.setEntry(2, 2 * i + 1, -l.getZ());
189 }
190
191
192 final SingularValueDecomposition svd = new SingularValueDecomposition(matrix);
193
194
195
196
197 final Vector3D singularVector = new Vector3D(svd.getU().getColumn(2)).normalize();
198
199
200 final Vector3D first = sensor.getLos(midDate, 0);
201 final Vector3D last = sensor.getLos(midDate, sensor.getNbPixels() - 1);
202 if (Vector3D.dotProduct(singularVector, Vector3D.crossProduct(first, last)) >= 0) {
203 return singularVector;
204 } else {
205 return singularVector.negate();
206 }
207
208 }
209
210
211
212
213 public LineSensor getSensor() {
214 return sensor;
215 }
216
217
218
219
220 public SpacecraftToObservedBody getScToBody() {
221 return scToBody;
222 }
223
224
225
226
227 public int getMinLine() {
228 return minLine;
229 }
230
231
232
233
234 public int getMaxLine() {
235 return maxLine;
236 }
237
238
239
240
241 public int getMaxEval() {
242 return maxEval;
243 }
244
245
246
247
248 public double getAccuracy() {
249 return accuracy;
250 }
251
252
253
254
255
256
257
258
259
260 public Vector3D getMeanPlaneNormal() {
261 return meanPlaneNormal;
262 }
263
264
265
266
267 public CrossingResult[] getCachedResults() {
268 return cachedResults;
269 }
270
271
272 public static class CrossingResult {
273
274
275 private final AbsoluteDate crossingDate;
276
277
278 private final double crossingLine;
279
280
281 private final Vector3D target;
282
283
284 private final FieldVector3D<DerivativeStructure> targetDirection;
285
286
287
288
289
290
291
292 public CrossingResult(final AbsoluteDate crossingDate, final double crossingLine,
293 final Vector3D target,
294 final FieldVector3D<DerivativeStructure> targetDirection) {
295 this.crossingDate = crossingDate;
296 this.crossingLine = crossingLine;
297 this.target = target;
298 this.targetDirection = targetDirection;
299 }
300
301
302
303
304 public AbsoluteDate getDate() {
305 return crossingDate;
306 }
307
308
309
310
311 public double getLine() {
312 return crossingLine;
313 }
314
315
316
317
318 public Vector3D getTarget() {
319 return target;
320 }
321
322
323
324
325
326 public FieldVector3D<DerivativeStructure> getTargetDirection() {
327 return targetDirection;
328 }
329
330 }
331
332
333
334
335
336
337
338
339 public CrossingResult find(final Vector3D target)
340 throws RuggedException {
341
342 double crossingLine = midLine;
343 Transform bodyToInert = midBodyToInert;
344 Transform scToInert = midScToInert;
345
346
347 int n = 0;
348 while (n < cachedResults.length && cachedResults[n] != null) {
349 ++n;
350 }
351 if (n >= 4) {
352
353
354 final double guessedCrossingLine = guessStartLine(n, target);
355 if (guessedCrossingLine >= minLine && guessedCrossingLine <= maxLine) {
356 crossingLine = guessedCrossingLine;
357 final AbsoluteDate date = sensor.getDate(crossingLine);
358 bodyToInert = scToBody.getBodyToInertial(date);
359 scToInert = scToBody.getScToInertial(date);
360 }
361 }
362
363 final PVCoordinates targetPV = new PVCoordinates(target, Vector3D.ZERO);
364
365
366
367
368
369
370 final double[] crossingLineHistory = new double[maxEval];
371 final DerivativeStructure[] betaHistory = new DerivativeStructure[maxEval];
372 boolean atMin = false;
373 boolean atMax = false;
374 for (int i = 0; i < maxEval; ++i) {
375
376 crossingLineHistory[i] = crossingLine;
377 final FieldVector3D<DerivativeStructure> targetDirection =
378 evaluateLine(crossingLine, targetPV, bodyToInert, scToInert);
379 betaHistory[i] = FieldVector3D.angle(targetDirection, meanPlaneNormal);
380
381 final double deltaL;
382 if (i == 0) {
383
384 deltaL = (0.5 * FastMath.PI - betaHistory[i].getValue()) / betaHistory[i].getPartialDerivative(1);
385 crossingLine += deltaL;
386 } else {
387
388 final double a0 = betaHistory[i - 1].getValue() - 0.5 * FastMath.PI;
389 final double l0 = crossingLineHistory[i - 1];
390 final double d0 = betaHistory[i - 1].getPartialDerivative(1);
391 final double a1 = betaHistory[i].getValue() - 0.5 * FastMath.PI;
392 final double l1 = crossingLineHistory[i];
393 final double d1 = betaHistory[i].getPartialDerivative(1);
394 final double a1Ma0 = a1 - a0;
395 crossingLine = ((l0 * (a1 - 3 * a0) - a0 * a1Ma0 / d0) * a1 * a1 +
396 (l1 * (3 * a1 - a0) - a1 * a1Ma0 / d1) * a0 * a0
397 ) / (a1Ma0 * a1Ma0 * a1Ma0);
398 deltaL = crossingLine - l1;
399 }
400 if (FastMath.abs(deltaL) <= accuracy) {
401
402 for (int k = cachedResults.length - 1; k > 0; --k) {
403 cachedResults[k] = cachedResults[k - 1];
404 }
405 cachedResults[0] = new CrossingResult(sensor.getDate(crossingLine), crossingLine, target, targetDirection);
406 return cachedResults[0];
407 }
408 for (int j = 0; j < i; ++j) {
409 if (FastMath.abs(crossingLine - crossingLineHistory[j]) <= 1.0) {
410
411
412 final CrossingResult slowResult = slowFind(targetPV, crossingLine);
413 if (slowResult == null) {
414 return null;
415 }
416 for (int k = cachedResults.length - 1; k > 0; --k) {
417 cachedResults[k] = cachedResults[k - 1];
418 }
419 cachedResults[0] = slowResult;
420 return cachedResults[0];
421 }
422 }
423
424 if (crossingLine < minLine) {
425 if (atMin) {
426
427
428 return null;
429 }
430 atMin = true;
431 crossingLine = minLine;
432 } else if (crossingLine > maxLine) {
433 if (atMax) {
434
435
436 return null;
437 }
438 atMax = true;
439 crossingLine = maxLine;
440 } else {
441
442 atMin = false;
443 atMax = false;
444 }
445
446 final AbsoluteDate date = sensor.getDate(crossingLine);
447 bodyToInert = scToBody.getBodyToInertial(date);
448 scToInert = scToBody.getScToInertial(date);
449 }
450
451 return null;
452
453 }
454
455
456
457
458
459
460 private double guessStartLine(final int n, final Vector3D target) {
461 try {
462
463
464 final RealMatrix m = new Array2DRowRealMatrix(n, 4);
465 final RealVector v = new ArrayRealVector(n);
466 for (int i = 0; i < n; ++i) {
467 m.setEntry(i, 0, cachedResults[i].getTarget().getX());
468 m.setEntry(i, 1, cachedResults[i].getTarget().getY());
469 m.setEntry(i, 2, cachedResults[i].getTarget().getZ());
470 m.setEntry(i, 3, 1.0);
471 v.setEntry(i, cachedResults[i].getLine());
472 }
473
474 final DecompositionSolver solver = new QRDecomposition(m, Precision.SAFE_MIN).getSolver();
475 final RealVector coefficients = solver.solve(v);
476
477
478 return target.getX() * coefficients.getEntry(0) +
479 target.getY() * coefficients.getEntry(1) +
480 target.getZ() * coefficients.getEntry(2) +
481 coefficients.getEntry(3);
482
483 } catch (SingularMatrixException sme) {
484
485 return Double.NaN;
486 }
487 }
488
489
490
491
492
493
494
495
496
497 public CrossingResult slowFind(final PVCoordinates targetPV, final double initialGuess)
498 throws RuggedException {
499 try {
500
501
502 final double startValue;
503 if (initialGuess < minLine || initialGuess > maxLine) {
504 startValue = midLine;
505 } else {
506 startValue = initialGuess;
507 }
508
509 final UnivariateSolver solver = new BracketingNthOrderBrentSolver(accuracy, 5);
510 double crossingLine = solver.solve(maxEval, new UnivariateFunction() {
511
512 @Override
513 public double value(final double x) throws RuggedExceptionWrapper {
514 try {
515 final AbsoluteDate date = sensor.getDate(x);
516 final FieldVector3D<DerivativeStructure> targetDirection =
517 evaluateLine(x, targetPV, scToBody.getBodyToInertial(date), scToBody.getScToInertial(date));
518 final DerivativeStructure beta = FieldVector3D.angle(targetDirection, meanPlaneNormal);
519 return 0.5 * FastMath.PI - beta.getValue();
520 } catch (RuggedException re) {
521 throw new RuggedExceptionWrapper(re);
522 }
523 }
524 }, minLine, maxLine, startValue);
525
526 final AbsoluteDate date = sensor.getDate(crossingLine);
527 final FieldVector3D<DerivativeStructure> targetDirection =
528 evaluateLine(crossingLine, targetPV, scToBody.getBodyToInertial(date), scToBody.getScToInertial(date));
529 return new CrossingResult(sensor.getDate(crossingLine), crossingLine, targetPV.getPosition(), targetDirection);
530
531 } catch (NoBracketingException nbe) {
532 return null;
533 } catch (RuggedExceptionWrapper rew) {
534 throw rew.getException();
535 }
536 }
537
538
539
540
541
542
543
544
545
546 private FieldVector3D<DerivativeStructure> evaluateLine(final double lineNumber, final PVCoordinates targetPV,
547 final Transform bodyToInert, final Transform scToInert) {
548
549
550 final PVCoordinates refInert =
551 scToInert.transformPVCoordinates(new PVCoordinates(sensor.getPosition(), Vector3D.ZERO));
552
553 final PVCoordinates targetInert;
554 if (lightTimeCorrection) {
555
556 final Vector3D iT = bodyToInert.transformPosition(targetPV.getPosition());
557 final double deltaT = refInert.getPosition().distance(iT) / Constants.SPEED_OF_LIGHT;
558 targetInert = bodyToInert.shiftedBy(-deltaT).transformPVCoordinates(targetPV);
559 } else {
560
561 targetInert = bodyToInert.transformPVCoordinates(targetPV);
562 }
563
564 final PVCoordinates lInert = new PVCoordinates(refInert, targetInert);
565 final Transform inertToSc = scToInert.getInverse();
566 final Vector3D obsLInert;
567 final Vector3D obsLInertDot;
568 if (aberrationOfLightCorrection) {
569
570
571
572
573
574 final PVCoordinates spacecraftPV = scToInert.transformPVCoordinates(PVCoordinates.ZERO);
575 final Vector3D l = lInert.getPosition().normalize();
576 final Vector3D lDot = normalizedDot(lInert.getPosition(), lInert.getVelocity());
577 final Vector3D kObs = new Vector3D(Constants.SPEED_OF_LIGHT, l, +1.0, spacecraftPV.getVelocity());
578 obsLInert = kObs.normalize();
579
580
581
582
583 obsLInertDot = normalizedDot(kObs, new Vector3D(Constants.SPEED_OF_LIGHT, lDot));
584
585 } else {
586
587
588 obsLInert = lInert.getPosition().normalize();
589 obsLInertDot = normalizedDot(lInert.getPosition(), lInert.getVelocity());
590
591 }
592 final Vector3D dir = inertToSc.transformVector(obsLInert);
593 final Vector3D dirDot = new Vector3D(+1.0, inertToSc.transformVector(obsLInertDot),
594 -1.0, Vector3D.crossProduct(inertToSc.getRotationRate(), dir));
595
596
597 final double rate = sensor.getRate(lineNumber);
598 return new FieldVector3D<DerivativeStructure>(new DerivativeStructure(1, 1, dir.getX(), dirDot.getX() / rate),
599 new DerivativeStructure(1, 1, dir.getY(), dirDot.getY() / rate),
600 new DerivativeStructure(1, 1, dir.getZ(), dirDot.getZ() / rate));
601
602 }
603
604
605
606
607
608
609 private Vector3D normalizedDot(final Vector3D u, final Vector3D uDot) {
610 final double n = u.getNorm();
611 return new Vector3D(1.0 / n, uDot, -Vector3D.dotProduct(u, uDot) / (n * n * n), u);
612 }
613
614 }