1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit;
18
19 import org.hamcrest.Description;
20 import org.hamcrest.Matcher;
21 import org.hamcrest.Matchers;
22 import org.hamcrest.SelfDescribing;
23 import org.hamcrest.TypeSafeDiagnosingMatcher;
24 import org.hamcrest.TypeSafeMatcher;
25 import org.hipparchus.geometry.euclidean.threed.Rotation;
26 import org.hipparchus.geometry.euclidean.threed.Vector3D;
27 import org.hipparchus.linear.RealMatrix;
28 import org.hipparchus.util.FastMath;
29 import org.hipparchus.util.Precision;
30 import org.orekit.attitudes.Attitude;
31 import org.orekit.bodies.GeodeticPoint;
32 import org.orekit.time.AbsoluteDate;
33 import org.orekit.utils.Constants;
34 import org.orekit.utils.PVCoordinates;
35
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39
40 import static org.hamcrest.CoreMatchers.is;
41 import static org.hamcrest.Matchers.array;
42
43
44
45
46
47
48 public class OrekitMatchers {
49
50
51
52
53
54
55
56
57
58 public static Matcher<GeodeticPoint> geodeticPoint(
59 final Matcher<Double> lat,
60 final Matcher<Double> lon,
61 final Matcher<Double> alt) {
62 return new TypeSafeDiagnosingMatcher<GeodeticPoint>() {
63 @Override
64 public void describeTo(Description description) {
65 description.appendList("GeodeticPoint[", ", ", "]",
66 Arrays.<SelfDescribing>asList(lat, lon, alt));
67 }
68
69 @Override
70 protected boolean matchesSafely(GeodeticPoint item,
71 Description mismatchDescription) {
72 if (!lat.matches(item.getLatitude())) {
73 mismatchDescription.appendText("the latitude ");
74 lat.describeMismatch(item.getLatitude(),
75 mismatchDescription);
76 return false;
77 }
78 if (!lon.matches(item.getLongitude())) {
79 mismatchDescription.appendText("the longitude ");
80 lon.describeMismatch(item.getLongitude(),
81 mismatchDescription);
82 return false;
83 }
84 if (!alt.matches(item.getAltitude())) {
85 mismatchDescription.appendText("the altitude ");
86 alt.describeMismatch(item.getAltitude(),
87 mismatchDescription);
88 return false;
89 }
90 return true;
91 }
92 };
93 }
94
95
96
97
98
99
100
101
102
103 public static Matcher<GeodeticPoint> geodeticPoint(double lat,
104 double lon,
105 double alt) {
106 return geodeticPoint(is(lat), is(lon), is(alt));
107 }
108
109
110
111
112
113
114
115
116
117 public static Matcher<GeodeticPoint> geodeticPointCloseTo(
118 GeodeticPoint expected, double absTol) {
119 double angularAbsTol = absTol / Constants.WGS84_EARTH_EQUATORIAL_RADIUS;
120 return geodeticPoint(closeTo(expected.getLatitude(), angularAbsTol),
121 closeTo(expected.getLongitude(), angularAbsTol),
122 closeTo(expected.getAltitude(), absTol));
123 }
124
125
126
127
128
129
130
131
132
133 public static Matcher<GeodeticPoint> geodeticPointCloseTo(
134 GeodeticPoint expected, int ulps) {
135 return geodeticPoint(relativelyCloseTo(expected.getLatitude(), ulps),
136 relativelyCloseTo(expected.getLongitude(), ulps),
137 relativelyCloseTo(expected.getAltitude(), ulps));
138 }
139
140
141
142
143
144
145
146
147
148 public static Matcher<Vector3D> vector(final Matcher<Double> x,
149 final Matcher<Double> y,
150 final Matcher<Double> z) {
151 return new TypeSafeDiagnosingMatcher<Vector3D>() {
152 @Override
153 public void describeTo(Description description) {
154 description.appendList("Vector3D[", ", ", "]",
155 Arrays.<SelfDescribing>asList(x, y, z));
156 }
157
158 @Override
159 protected boolean matchesSafely(Vector3D item,
160 Description mismatchDescription) {
161 if (!x.matches(item.getX())) {
162 mismatchDescription.appendText("the x coordinate ");
163 x.describeMismatch(item.getX(), mismatchDescription);
164 return false;
165 }
166 if (!y.matches(item.getY())) {
167 mismatchDescription.appendText("the y coordinate ");
168 y.describeMismatch(item.getY(), mismatchDescription);
169 return false;
170 }
171 if (!z.matches(item.getZ())) {
172 mismatchDescription.appendText("the z coordinate ");
173 z.describeMismatch(item.getZ(), mismatchDescription);
174 return false;
175 }
176 return true;
177 }
178 };
179 }
180
181
182
183
184
185
186
187
188 public static Matcher<Vector3D> vectorCloseTo(Vector3D vector, double absTol) {
189 return vector(closeTo(vector.getX(), absTol),
190 closeTo(vector.getY(), absTol), closeTo(vector.getZ(), absTol));
191 }
192
193
194
195
196
197
198
199
200
201 public static Matcher<Vector3D> vectorCloseTo(Vector3D vector, int ulps) {
202 return vector(relativelyCloseTo(vector.getX(), ulps),
203 relativelyCloseTo(vector.getY(), ulps),
204 relativelyCloseTo(vector.getZ(), ulps));
205 }
206
207
208
209
210
211
212
213
214
215
216 public static Matcher<Vector3D> vectorCloseTo(double x, double y, double z, int ulps) {
217 return vectorCloseTo(new Vector3D(x, y, z), ulps);
218 }
219
220
221
222
223
224
225
226
227
228
229
230 public static Matcher<Vector3D> vectorCloseTo(Vector3D vector,
231 double absTol, int ulps) {
232 return vector(numberCloseTo(vector.getX(), absTol, ulps),
233 numberCloseTo(vector.getY(), absTol, ulps),
234 numberCloseTo(vector.getZ(), absTol, ulps));
235 }
236
237
238
239
240
241
242
243
244 public static Matcher<PVCoordinates> pvIs(
245 final Matcher<? super Vector3D> position,
246 final Matcher<? super Vector3D> velocity) {
247 return new TypeSafeDiagnosingMatcher<PVCoordinates>() {
248
249 @Override
250 public void describeTo(Description description) {
251 description.appendText("position ");
252 description.appendDescriptionOf(position);
253 description.appendText(" and velocity ");
254 description.appendDescriptionOf(velocity);
255 }
256
257 @Override
258 protected boolean matchesSafely(PVCoordinates item,
259 Description mismatchDescription) {
260 if (!position.matches(item.getPosition())) {
261
262 mismatchDescription.appendText("position ");
263 position.describeMismatch(item.getPosition(), mismatchDescription);
264 return false;
265 } else if (!velocity.matches(item.getVelocity())) {
266
267 mismatchDescription.appendText("velocity ");
268 velocity.describeMismatch(item.getVelocity(), mismatchDescription);
269 return false;
270 } else {
271
272 return true;
273 }
274 }
275 };
276 }
277
278
279
280
281
282
283
284 public static Matcher<PVCoordinates> pvIs(PVCoordinates pv) {
285 return pvCloseTo(pv, 0);
286 }
287
288
289
290
291
292
293
294
295
296 public static Matcher<PVCoordinates> pvCloseTo(PVCoordinates pv,
297 double absTol) {
298 return pvIs(vectorCloseTo(pv.getPosition(), absTol),
299 vectorCloseTo(pv.getVelocity(), absTol));
300 }
301
302
303
304
305
306
307
308
309 public static Matcher<PVCoordinates> pvCloseTo(PVCoordinates pv, int ulps) {
310 return pvIs(vectorCloseTo(pv.getPosition(), ulps),
311 vectorCloseTo(pv.getVelocity(), ulps));
312 }
313
314
315
316
317
318
319
320
321
322
323 public static Matcher<Double> relativelyCloseTo(final double expected,
324 final int ulps) {
325 return new TypeSafeDiagnosingMatcher<Double>() {
326
327 @Override
328 public void describeTo(Description description) {
329 description.appendText("a numeric value within ")
330 .appendValue(ulps).appendText(" ulps of ")
331 .appendValue(expected);
332 }
333
334 @Override
335 protected boolean matchesSafely(Double item,
336 Description mismatchDescription) {
337 if (!Precision.equals(item, expected, ulps)) {
338 mismatchDescription
339 .appendValue(item)
340 .appendText(" was off by ")
341 .appendValue(
342 Double.doubleToLongBits(item)
343 - Double.doubleToLongBits(expected))
344 .appendText(" ulps");
345 return false;
346 }
347 return true;
348 }
349 };
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363 public static Matcher<Double> numberCloseTo(double number, double absTol,
364 int ulps) {
365 Collection<Matcher<Double>> matchers = new ArrayList<>(2);
366 matchers.add(closeTo(number, absTol));
367 matchers.add(relativelyCloseTo(number, ulps));
368 return anyOf(matchers);
369 }
370
371
372
373
374
375
376
377
378 @SafeVarargs
379 public static Matcher<double[]> doubleArrayContaining(
380 Matcher<? super Double>... matchers) {
381 return new TypeSafeDiagnosingMatcher<double[]>() {
382
383 @Override
384 public void describeTo(Description description) {
385 description.appendList("[", ", ", "]", Arrays.<Matcher<?>>asList(matchers));
386 }
387
388 @Override
389 protected boolean matchesSafely(double[] actual,
390 Description mismatchDescription) {
391 if (actual.length != matchers.length) {
392 mismatchDescription.appendText("array length was " + actual.length);
393 return false;
394 }
395 for (int i = 0; i < actual.length; i++) {
396 if (!matchers[i].matches(actual[i])) {
397 mismatchDescription.appendText("in element " + i + " ");
398 matchers[i].describeMismatch(actual[i],
399 mismatchDescription);
400 return false;
401 }
402 }
403 return true;
404 }
405 };
406 }
407
408
409
410
411
412
413
414
415 public static Matcher<double[]> doubleArrayContaining(double... values) {
416 @SuppressWarnings("unchecked")
417 Matcher<Double>[] elementMatchers = new Matcher[values.length];
418 for (int i = 0; i < elementMatchers.length; i++) {
419 elementMatchers[i] = Matchers.is(values[i]);
420 }
421 return doubleArrayContaining(elementMatchers);
422 }
423
424
425
426
427
428
429
430
431
432 public static Matcher<double[]> doubleArrayContaining(
433 final double[] values, final double absTol) {
434 @SuppressWarnings("unchecked")
435 Matcher<Double>[] elementMatchers = new Matcher[values.length];
436 for (int i = 0; i < elementMatchers.length; i++) {
437 elementMatchers[i] = closeTo(values[i], absTol);
438 }
439 return doubleArrayContaining(elementMatchers);
440 }
441
442
443
444
445
446
447
448
449
450
451
452 public static Matcher<double[]> doubleArrayContaining(
453 final double[] values, final int ulps) {
454 @SuppressWarnings("unchecked")
455 Matcher<Double>[] elementMatchers = new Matcher[values.length];
456 for (int i = 0; i < elementMatchers.length; i++) {
457 elementMatchers[i] = relativelyCloseTo(values[i], ulps);
458 }
459 return doubleArrayContaining(elementMatchers);
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473 public static Matcher<double[]> doubleArrayContaining(
474 final double[] values, final double absTol, final int ulps) {
475 @SuppressWarnings("unchecked")
476 Matcher<Double>[] elementMatchers = new Matcher[values.length];
477 for (int i = 0; i < elementMatchers.length; i++) {
478 elementMatchers[i] = numberCloseTo(values[i], absTol, ulps);
479 }
480 return doubleArrayContaining(elementMatchers);
481 }
482
483
484
485
486
487
488
489 public static Matcher<RealMatrix> matrix(
490 final Matcher<double[][]> dataMatcher) {
491 return new TypeSafeDiagnosingMatcher<RealMatrix>() {
492 @Override
493 public void describeTo(Description description) {
494 description.appendText("matrix that ").appendDescriptionOf(
495 dataMatcher);
496 }
497
498 @Override
499 protected boolean matchesSafely(RealMatrix item,
500 Description mismatchDescription) {
501 if (dataMatcher.matches(item.getData())) {
502 return true;
503 } else {
504 mismatchDescription.appendText("matrix did not match ");
505 dataMatcher.describeMismatch(item.getData(),
506 mismatchDescription);
507 return false;
508 }
509 }
510 };
511 }
512
513
514
515
516
517
518
519
520
521
522 public static Matcher<RealMatrix> matrixCloseTo(RealMatrix expected,
523 double absTol) {
524 @SuppressWarnings("rawtypes")
525 Matcher[] rowMatchers = new Matcher[expected.getRowDimension()];
526 for (int i = 0; i < rowMatchers.length; i++) {
527 rowMatchers[i] = doubleArrayContaining(expected.getRow(i), absTol);
528 }
529 @SuppressWarnings("unchecked")
530 Matcher<double[][]> dataMatcher = array(rowMatchers);
531 return matrix(dataMatcher);
532 }
533
534
535
536
537
538
539
540
541
542
543
544 public static Matcher<RealMatrix> matrixCloseTo(RealMatrix expected,
545 double absTol, int ulps) {
546 @SuppressWarnings("rawtypes")
547 Matcher[] rowMatchers = new Matcher[expected.getRowDimension()];
548 for (int i = 0; i < rowMatchers.length; i++) {
549 rowMatchers[i] = doubleArrayContaining(expected.getRow(i), absTol,
550 ulps);
551 }
552 @SuppressWarnings("unchecked")
553 Matcher<double[][]> dataMatcher = array(rowMatchers);
554 return matrix(dataMatcher);
555 }
556
557
558
559
560
561
562
563
564
565 public static <T> Matcher<T> anyOf(Collection<? extends Matcher<? super T>> matchers) {
566 return new TypeSafeDiagnosingMatcher<T>() {
567 @Override
568 protected boolean matchesSafely(final T item,
569 final Description mismatchDescription) {
570 boolean first = true;
571 for (Matcher<? super T> matcher : matchers) {
572 if (matcher.matches(item)) {
573 return true;
574 } else {
575 if (!first) {
576 mismatchDescription.appendText(" and ");
577 }
578 matcher.describeMismatch(item, mismatchDescription);
579 }
580 first = false;
581 }
582 return false;
583 }
584
585 @Override
586 public void describeTo(final Description description) {
587 description.appendList("(", " or ", ")", matchers);
588 }
589 };
590 }
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 public static Matcher<Double> closeTo(final double value,
607 final double delta) {
608
609 return new TypeSafeMatcher<Double>() {
610 @Override
611 public boolean matchesSafely(Double item) {
612 return actualDelta(item) <= 0.0;
613 }
614
615 @Override
616 public void describeMismatchSafely(Double item, Description mismatchDescription) {
617 mismatchDescription.appendValue(item)
618 .appendText(" differed by ")
619 .appendValue(actualDelta(item));
620 }
621
622 @Override
623 public void describeTo(Description description) {
624 description.appendText("a numeric value within ")
625 .appendValue(delta)
626 .appendText(" of ")
627 .appendValue(value);
628 }
629
630 private double actualDelta(Double item) {
631 return (FastMath.abs((item - value)) - delta);
632 }
633 };
634
635 }
636
637
638
639
640
641
642
643
644
645
646
647 public static <T extends Comparable<T>> Matcher<T> greaterThan(final T expected) {
648 return new TypeSafeDiagnosingMatcher<T>() {
649 @Override
650 protected boolean matchesSafely(T item, Description mismatchDescription) {
651 if (expected.compareTo(item) >= 0) {
652 mismatchDescription.appendText("less than or equal to ")
653 .appendValue(expected);
654 return false;
655 }
656 return true;
657 }
658
659 @Override
660 public void describeTo(Description description) {
661 description.appendText("greater than ").appendValue(expected);
662 }
663 };
664 }
665
666
667
668
669
670
671
672
673
674
675
676
677 public static Matcher<AbsoluteDate> durationFrom(final AbsoluteDate date,
678 final Matcher<Double> valueMatcher) {
679 return new TypeSafeDiagnosingMatcher<AbsoluteDate>() {
680 @Override
681 public void describeTo(Description description) {
682 description.appendText("delta from ");
683 description.appendValue(date);
684 description.appendText(" is ");
685 description.appendDescriptionOf(valueMatcher);
686 }
687
688 @Override
689 protected boolean matchesSafely(AbsoluteDate item,
690 Description mismatchDescription) {
691 double delta = item.durationFrom(date);
692 boolean matched = valueMatcher.matches(delta);
693 if (!matched) {
694 mismatchDescription.appendText("delta to ");
695 mismatchDescription.appendValue(item);
696 mismatchDescription.appendText(" ");
697 valueMatcher.describeMismatch(delta, mismatchDescription);
698 }
699 return matched;
700 }
701 };
702 }
703
704
705
706
707
708
709
710
711
712
713 public static Matcher<Rotation> distanceIs(final Rotation from,
714 final Matcher<Double> valueMatcher) {
715 return new TypeSafeDiagnosingMatcher<Rotation>() {
716
717 @Override
718 public void describeTo(Description description) {
719 description.appendText("distance from ");
720 description.appendValue(from);
721 description.appendText(" is ");
722 description.appendDescriptionOf(valueMatcher);
723 }
724
725 @Override
726 protected boolean matchesSafely(Rotation item,
727 Description mismatchDescription) {
728 double distance = Rotation.distance(from, item);
729 boolean matched = valueMatcher.matches(distance);
730 if (!matched) {
731 mismatchDescription.appendText("distance to ");
732 mismatchDescription.appendValue(item);
733 mismatchDescription.appendText(" was ");
734 valueMatcher
735 .describeMismatch(distance, mismatchDescription);
736 }
737 return matched;
738 }
739 };
740 }
741
742
743
744
745
746
747
748 public static Matcher<Attitude> attitudeIs(final Attitude expected) {
749 final Matcher<AbsoluteDate> dateMatcher = durationFrom(expected.getDate(), is(0.0));
750 final Matcher<Rotation> rotationMatcher = distanceIs(expected.getRotation(), is(0.0));
751 final Matcher<Vector3D> spinMatcher = vectorCloseTo(expected.getSpin(), 0);
752 final Matcher<Vector3D> accelerationMatcher =
753 vectorCloseTo(expected.getRotationAcceleration(), 0);
754 final Rotation r = expected.getRotation();
755 return new TypeSafeDiagnosingMatcher<Attitude>() {
756
757 @Override
758 protected boolean matchesSafely(Attitude item,
759 final Description mismatchDescription) {
760 item = item.withReferenceFrame(expected.getReferenceFrame());
761 if (!dateMatcher.matches(item)) {
762 mismatchDescription.appendText("date ")
763 .appendDescriptionOf(dateMatcher);
764 }
765 if (!rotationMatcher.matches(item.getRotation())) {
766 mismatchDescription.appendText("rotation ")
767 .appendDescriptionOf(rotationMatcher);
768 return false;
769 }
770 if (!spinMatcher.matches(item.getSpin())) {
771 mismatchDescription.appendText("spin ")
772 .appendDescriptionOf(spinMatcher);
773 return false;
774 }
775 if (!accelerationMatcher.matches(item.getRotationAcceleration())) {
776 mismatchDescription.appendText("rotation acceleration ")
777 .appendDescriptionOf(accelerationMatcher);
778 return false;
779 }
780 return true;
781 }
782
783 @Override
784 public void describeTo(Description description) {
785 description.appendText("attitude on ").appendValue(expected.getDate())
786 .appendText(" rotation ").appendValueList("[", ",", "]", r.getQ0(), r.getQ1(), r.getQ2(), r.getQ3())
787 .appendText(" spin ").appendDescriptionOf(spinMatcher)
788 .appendText(" acceleration ").appendDescriptionOf(accelerationMatcher);
789 }
790 };
791 }
792
793
794 }