1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.rugged.errors;
18
19 import java.io.BufferedReader;
20 import java.io.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.io.ObjectOutputStream;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.nio.charset.StandardCharsets;
29 import java.nio.file.Files;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.NavigableMap;
36 import java.util.TreeMap;
37 import java.util.regex.Pattern;
38 import java.util.stream.Stream;
39
40 import org.hipparchus.analysis.differentiation.Derivative;
41 import org.hipparchus.exception.LocalizedCoreFormats;
42 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
43 import org.hipparchus.geometry.euclidean.threed.Rotation;
44 import org.hipparchus.geometry.euclidean.threed.Vector3D;
45 import org.hipparchus.util.FastMath;
46 import org.hipparchus.util.OpenIntToDoubleHashMap;
47 import org.hipparchus.util.Pair;
48 import org.orekit.bodies.GeodeticPoint;
49 import org.orekit.bodies.OneAxisEllipsoid;
50 import org.orekit.frames.Frame;
51 import org.orekit.frames.FramesFactory;
52 import org.orekit.frames.Predefined;
53 import org.orekit.frames.Transform;
54 import org.orekit.rugged.api.AlgorithmId;
55 import org.orekit.rugged.api.Rugged;
56 import org.orekit.rugged.api.RuggedBuilder;
57 import org.orekit.rugged.linesensor.LineDatation;
58 import org.orekit.rugged.linesensor.LineSensor;
59 import org.orekit.rugged.linesensor.SensorMeanPlaneCrossing;
60 import org.orekit.rugged.linesensor.SensorMeanPlaneCrossing.CrossingResult;
61 import org.orekit.rugged.linesensor.SensorPixel;
62 import org.orekit.rugged.los.TimeDependentLOS;
63 import org.orekit.rugged.raster.TileUpdater;
64 import org.orekit.rugged.raster.UpdatableTile;
65 import org.orekit.rugged.refraction.AtmosphericRefraction;
66 import org.orekit.rugged.refraction.MultiLayerModel;
67 import org.orekit.rugged.utils.DerivativeGenerator;
68 import org.orekit.rugged.utils.ExtendedEllipsoid;
69 import org.orekit.rugged.utils.SpacecraftToObservedBody;
70 import org.orekit.time.AbsoluteDate;
71 import org.orekit.time.TimeScalesFactory;
72 import org.orekit.utils.ParameterDriver;
73
74
75
76
77
78
79
80 public class DumpReplayer {
81
82
83 private static final String COMMENT_START = "#";
84
85
86 private static final String LATITUDE = "latitude";
87
88
89 private static final String LONGITUDE = "longitude";
90
91
92 private static final String ELEVATION = "elevation";
93
94
95 private static final String AE = "ae";
96
97
98 private static final String F = "f";
99
100
101 private static final String FRAME = "frame";
102
103
104 private static final String DATE = "date";
105
106
107 private static final String POSITION = "position";
108
109
110 private static final String LOS = "los";
111
112
113 private static final String LIGHT_TIME = "lightTime";
114
115
116 private static final String ABERRATION = "aberration";
117
118
119 private static final String REFRACTION = "refraction";
120
121
122 private static final String MIN_DATE = "minDate";
123
124
125 private static final String MAX_DATE = "maxDate";
126
127
128 private static final String T_STEP = "tStep";
129
130
131 private static final String TOLERANCE = "tolerance";
132
133
134 private static final String INERTIAL_FRAME = "inertialFrame";
135
136
137 private static final String INDEX = "index";
138
139
140 private static final String BODY = "body";
141
142
143 private static final String R = "r";
144
145
146 private static final String OMEGA = "Ω";
147
148
149 private static final String OMEGA_DOT = "ΩDot";
150
151
152 private static final String SPACECRAFT = "spacecraft";
153
154
155 private static final String P = "p";
156
157
158 private static final String V = "v";
159
160
161 private static final String A = "a";
162
163
164 private static final String LAT_MIN = "latMin";
165
166
167 private static final String LAT_STEP = "latStep";
168
169
170 private static final String LAT_ROWS = "latRows";
171
172
173 private static final String LON_MIN = "lonMin";
174
175
176 private static final String LON_STEP = "lonStep";
177
178
179 private static final String LON_COLS = "lonCols";
180
181
182 private static final String LAT_INDEX = "latIndex";
183
184
185 private static final String LON_INDEX = "lonIndex";
186
187
188 private static final String SENSOR_NAME = "sensorName";
189
190
191 private static final String MIN_LINE = "minLine";
192
193
194 private static final String MAX_LINE = "maxLine";
195
196
197 private static final String LINE_NUMBER = "lineNumber";
198
199
200 private static final String NB_PIXELS = "nbPixels";
201
202
203 private static final String PIXEL_NUMBER = "pixelNumber";
204
205
206 private static final String MAX_EVAL = "maxEval";
207
208
209 private static final String ACCURACY = "accuracy";
210
211
212 private static final String NORMAL = "normal";
213
214
215 private static final String RATE = "rate";
216
217
218 private static final String CACHED_RESULTS = "cachedResults";
219
220
221 private static final String TARGET = "target";
222
223
224 private static final String TARGET_DIRECTION = "targetDirection";
225
226
227 private static final String NULL_RESULT = "NULL";
228
229
230 private static final Pattern SEPARATOR = Pattern.compile("\\s+");
231
232
233 private static final Pattern PATTERN = Pattern.compile(" ");
234
235
236 private double constantElevation;
237
238
239 private AlgorithmId algorithmId;
240
241
242 private OneAxisEllipsoid ellipsoid;
243
244
245 private final List<ParsedTile> tiles;
246
247
248 private final List<ParsedSensor> sensors;
249
250
251 private AbsoluteDate minDate;
252
253
254 private AbsoluteDate maxDate;
255
256
257 private double tStep;
258
259
260 private double tolerance;
261
262
263 private Frame inertialFrame;
264
265
266 private NavigableMap<Integer, Transform> bodyToInertial;
267
268
269 private NavigableMap<Integer, Transform> scToInertial;
270
271
272 private boolean lightTimeCorrection;
273
274
275 private boolean aberrationOfLightCorrection;
276
277
278 private boolean atmosphericRefraction;
279
280
281 private final List<DumpedCall> calls;
282
283
284
285
286 public DumpReplayer() {
287 tiles = new ArrayList<>();
288 sensors = new ArrayList<>();
289 calls = new ArrayList<>();
290 }
291
292
293
294
295 public void parse(final File file) {
296 try (BufferedReader reader =
297 new BufferedReader(new InputStreamReader(Files.newInputStream(file.toPath()),
298 StandardCharsets.UTF_8))) {
299
300 int l = 0;
301 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
302 LineParser.parse(++l, file, line, this);
303 }
304 } catch (IOException ioe) {
305 throw new RuggedException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
306 }
307 }
308
309
310
311
312 public Rugged createRugged() {
313 try {
314 final RuggedBuilder builder = new RuggedBuilder();
315
316 if (algorithmId == null) {
317 algorithmId = AlgorithmId.IGNORE_DEM_USE_ELLIPSOID;
318 }
319 builder.setAlgorithm(algorithmId);
320 if (algorithmId == AlgorithmId.CONSTANT_ELEVATION_OVER_ELLIPSOID) {
321 builder.setConstantElevation(constantElevation);
322 } else if (algorithmId != AlgorithmId.IGNORE_DEM_USE_ELLIPSOID) {
323
324
325
326 builder.setDigitalElevationModel(new TileUpdater() {
327
328
329 @Override
330 public void updateTile(final double latitude, final double longitude, final UpdatableTile tile) {
331 for (final ParsedTile parsedTile : tiles) {
332 if (parsedTile.isInterpolable(latitude, longitude)) {
333 parsedTile.updateTile(tile);
334 return;
335 }
336 }
337 throw new RuggedException(RuggedMessages.NO_DEM_DATA,
338 FastMath.toDegrees(latitude), FastMath.toDegrees(longitude));
339 }
340 }, 8);
341 }
342
343 builder.setEllipsoid(ellipsoid);
344
345 builder.setLightTimeCorrection(lightTimeCorrection);
346 builder.setAberrationOfLightCorrection(aberrationOfLightCorrection);
347 if (atmosphericRefraction) {
348 final ExtendedEllipsoid extendedEllipsoid = builder.getEllipsoid();
349 final AtmosphericRefraction atmosphericModel = new MultiLayerModel(extendedEllipsoid);
350
351 builder.setRefractionCorrection(atmosphericModel);
352 }
353
354
355
356 final int n = (int) FastMath.ceil(maxDate.durationFrom(minDate) / tStep);
357 final List<Transform> b2iList = new ArrayList<>(n);
358 final List<Transform> s2iList = new ArrayList<>(n);
359 for (int i = 0; i < n; ++i) {
360 if (bodyToInertial.containsKey(i)) {
361
362 b2iList.add(bodyToInertial.get(i));
363 s2iList.add(scToInertial.get(i));
364 } else {
365
366 final Map.Entry<Integer, Transform> lower = bodyToInertial.lowerEntry(i);
367 final Map.Entry<Integer, Transform> higher = bodyToInertial.higherEntry(i);
368 final int closest;
369 if (lower == null) {
370 closest = higher.getKey();
371 } else if (higher == null) {
372 closest = lower.getKey();
373 } else {
374 closest = (i - lower.getKey() <= higher.getKey() - i) ? lower.getKey() : higher.getKey();
375 }
376 b2iList.add(bodyToInertial.get(closest).shiftedBy((i - closest) * tStep));
377 s2iList.add(scToInertial.get(closest).shiftedBy((i - closest) * tStep));
378 }
379 }
380
381
382
383 final SpacecraftToObservedBody scToBody =
384 new SpacecraftToObservedBody(inertialFrame, ellipsoid.getBodyFrame(),
385 minDate, maxDate, tStep, tolerance,
386 b2iList, s2iList);
387 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
388 new ObjectOutputStream(bos).writeObject(scToBody);
389 final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
390 builder.setTrajectoryAndTimeSpan(bis);
391
392 final List<SensorMeanPlaneCrossing> planeCrossings = new ArrayList<>();
393 for (final ParsedSensor parsedSensor : sensors) {
394 final LineSensor sensor = new LineSensor(parsedSensor.name,
395 parsedSensor,
396 parsedSensor.position,
397 parsedSensor);
398 if (parsedSensor.meanPlane != null) {
399 planeCrossings.add(new SensorMeanPlaneCrossing(sensor, scToBody,
400 parsedSensor.meanPlane.minLine,
401 parsedSensor.meanPlane.maxLine,
402 lightTimeCorrection, aberrationOfLightCorrection,
403 parsedSensor.meanPlane.maxEval,
404 parsedSensor.meanPlane.accuracy,
405 parsedSensor.meanPlane.normal,
406 Arrays.stream(parsedSensor.meanPlane.cachedResults)));
407 }
408 builder.addLineSensor(sensor);
409 }
410
411 final Rugged rugged = builder.build();
412
413 final Method setPlaneCrossing = Rugged.class.getDeclaredMethod("setPlaneCrossing",
414 SensorMeanPlaneCrossing.class);
415 setPlaneCrossing.setAccessible(true);
416 for (final SensorMeanPlaneCrossing planeCrossing : planeCrossings) {
417 setPlaneCrossing.invoke(rugged, planeCrossing);
418 }
419
420 return rugged;
421
422 } catch (IOException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
423
424 throw new RuggedInternalError(e);
425 }
426 }
427
428
429
430
431
432 private ParsedSensor getSensor(final String name) {
433 for (final ParsedSensor sensor : sensors) {
434 if (sensor.name.equals(name)) {
435 return sensor;
436 }
437 }
438 final ParsedSensor sensor = new ParsedSensor(name);
439 sensors.add(sensor);
440 return sensor;
441 }
442
443
444
445
446
447
448
449
450
451 public Result[] execute(final Rugged rugged) {
452 final Result[] results = new Result[calls.size()];
453 for (int i = 0; i < calls.size(); ++i) {
454 results[i] = new Result(calls.get(i).expected,
455 calls.get(i).execute(rugged));
456 }
457 return results;
458 }
459
460
461 public static class Result {
462
463
464 private final Object expected;
465
466
467 private final Object replayed;
468
469
470
471
472
473 private Result(final Object expected, final Object replayed) {
474 this.expected = expected;
475 this.replayed = replayed;
476 }
477
478
479
480
481 public Object getExpected() {
482 return expected;
483 }
484
485
486
487
488 public Object getReplayed() {
489 return replayed;
490 }
491
492 }
493
494
495 private enum LineParser {
496
497
498 ALGORITHM() {
499
500
501 @Override
502 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
503 try {
504 if (fields.length < 1) {
505 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
506 }
507 global.algorithmId = AlgorithmId.valueOf(fields[0]);
508 if (global.algorithmId == AlgorithmId.CONSTANT_ELEVATION_OVER_ELLIPSOID) {
509 if (fields.length < 3 || !fields[1].equals(ELEVATION)) {
510 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
511 }
512 global.constantElevation = Double.parseDouble(fields[2]);
513 }
514 } catch (IllegalArgumentException iae) {
515 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
516 }
517 }
518
519 },
520
521
522 ELLIPSOID() {
523
524
525 @Override
526 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
527 if (fields.length < 6 || !fields[0].equals(AE) || !fields[2].equals(F) || !fields[4].equals(FRAME)) {
528 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
529 }
530 final double ae = Double.parseDouble(fields[1]);
531 final double f = Double.parseDouble(fields[3]);
532 final Frame bodyFrame;
533 try {
534 bodyFrame = FramesFactory.getFrame(Predefined.valueOf(fields[5]));
535 } catch (IllegalArgumentException iae) {
536 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
537 }
538 global.ellipsoid = new OneAxisEllipsoid(ae, f, bodyFrame);
539 }
540
541 },
542
543
544 DIRECT_LOCATION() {
545
546
547 @Override
548 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
549 if (fields.length < 16 ||
550 !fields[0].equals(DATE) ||
551 !fields[2].equals(POSITION) || !fields[6].equals(LOS) ||
552 !fields[10].equals(LIGHT_TIME) || !fields[12].equals(ABERRATION) ||
553 !fields[14].equals(REFRACTION)) {
554 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
555 }
556 final AbsoluteDate date = new AbsoluteDate(fields[1], TimeScalesFactory.getUTC());
557 final Vector3D position = new Vector3D(Double.parseDouble(fields[3]),
558 Double.parseDouble(fields[4]),
559 Double.parseDouble(fields[5]));
560 final Vector3D los = new Vector3D(Double.parseDouble(fields[7]),
561 Double.parseDouble(fields[8]),
562 Double.parseDouble(fields[9]));
563 if (global.calls.isEmpty()) {
564 global.lightTimeCorrection = Boolean.parseBoolean(fields[11]);
565 global.aberrationOfLightCorrection = Boolean.parseBoolean(fields[13]);
566 global.atmosphericRefraction = Boolean.parseBoolean(fields[15]);
567 } else {
568 if (global.lightTimeCorrection != Boolean.parseBoolean(fields[11])) {
569 throw new RuggedException(RuggedMessages.LIGHT_TIME_CORRECTION_REDEFINED,
570 l, file.getAbsolutePath(), line);
571 }
572 if (global.aberrationOfLightCorrection != Boolean.parseBoolean(fields[13])) {
573 throw new RuggedException(RuggedMessages.ABERRATION_OF_LIGHT_CORRECTION_REDEFINED,
574 l, file.getAbsolutePath(), line);
575 }
576 if (global.atmosphericRefraction != Boolean.parseBoolean(fields[15])) {
577 throw new RuggedException(RuggedMessages.ATMOSPHERIC_REFRACTION_REDEFINED,
578 l, file.getAbsolutePath(), line);
579 }
580 }
581 global.calls.add(new DumpedCall() {
582
583
584 @Override
585 public Object execute(final Rugged rugged) {
586 return rugged.directLocation(date, position, los);
587 }
588
589 });
590 }
591 },
592
593
594 DIRECT_LOCATION_RESULT() {
595
596
597 @Override
598 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
599 if (fields.length == 1) {
600 if (fields[0].equals(NULL_RESULT)) {
601 final GeodeticPoint gp = null;
602 final DumpedCall last = global.calls.get(global.calls.size() - 1);
603 last.expected = gp;
604 } else {
605 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
606 }
607 } else if (fields.length < 6 || !fields[0].equals(LATITUDE) ||
608 !fields[2].equals(LONGITUDE) || !fields[4].equals(ELEVATION)) {
609 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
610 } else {
611 final GeodeticPoint gp = new GeodeticPoint(Double.parseDouble(fields[1]),
612 Double.parseDouble(fields[3]),
613 Double.parseDouble(fields[5]));
614 final DumpedCall last = global.calls.get(global.calls.size() - 1);
615 last.expected = gp;
616 }
617 }
618
619 },
620
621
622 SPAN() {
623
624
625 @Override
626 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
627 if (fields.length < 10 ||
628 !fields[0].equals(MIN_DATE) || !fields[2].equals(MAX_DATE) || !fields[4].equals(T_STEP) ||
629 !fields[6].equals(TOLERANCE) || !fields[8].equals(INERTIAL_FRAME)) {
630 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
631 }
632 global.minDate = new AbsoluteDate(fields[1], TimeScalesFactory.getUTC());
633 global.maxDate = new AbsoluteDate(fields[3], TimeScalesFactory.getUTC());
634 global.tStep = Double.parseDouble(fields[5]);
635 global.tolerance = Double.parseDouble(fields[7]);
636 global.bodyToInertial = new TreeMap<>();
637 global.scToInertial = new TreeMap<>();
638 try {
639 global.inertialFrame = FramesFactory.getFrame(Predefined.valueOf(fields[9]));
640 } catch (IllegalArgumentException iae) {
641 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
642 }
643 }
644 },
645
646
647 TRANSFORM() {
648
649
650 @Override
651 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
652 if (fields.length < 42 ||
653 !fields[0].equals(INDEX) ||
654 !fields[2].equals(BODY) ||
655 !fields[3].equals(R) || !fields[8].equals(OMEGA) || !fields[12].equals(OMEGA_DOT) ||
656 !fields[16].equals(SPACECRAFT) ||
657 !fields[17].equals(P) || !fields[21].equals(V) || !fields[25].equals(A) ||
658 !fields[29].equals(R) || !fields[34].equals(OMEGA) || !fields[38].equals(OMEGA_DOT)) {
659 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
660 }
661 final int i = Integer.parseInt(fields[1]);
662 final AbsoluteDate date = global.minDate.shiftedBy(i * global.tStep);
663 global.bodyToInertial.put(i,
664 new Transform(date,
665 new Rotation(Double.parseDouble(fields[4]),
666 Double.parseDouble(fields[5]),
667 Double.parseDouble(fields[6]),
668 Double.parseDouble(fields[7]),
669 false),
670 new Vector3D(Double.parseDouble(fields[9]),
671 Double.parseDouble(fields[10]),
672 Double.parseDouble(fields[11])),
673 new Vector3D(Double.parseDouble(fields[13]),
674 Double.parseDouble(fields[14]),
675 Double.parseDouble(fields[15]))));
676 global.scToInertial.put(i,
677 new Transform(date,
678 new Transform(date,
679 new Vector3D(Double.parseDouble(fields[18]),
680 Double.parseDouble(fields[19]),
681 Double.parseDouble(fields[20])),
682 new Vector3D(Double.parseDouble(fields[22]),
683 Double.parseDouble(fields[23]),
684 Double.parseDouble(fields[24])),
685 new Vector3D(Double.parseDouble(fields[26]),
686 Double.parseDouble(fields[27]),
687 Double.parseDouble(fields[28]))),
688 new Transform(date,
689 new Rotation(Double.parseDouble(fields[30]),
690 Double.parseDouble(fields[31]),
691 Double.parseDouble(fields[32]),
692 Double.parseDouble(fields[33]),
693 false),
694 new Vector3D(Double.parseDouble(fields[35]),
695 Double.parseDouble(fields[36]),
696 Double.parseDouble(fields[37])),
697 new Vector3D(Double.parseDouble(fields[39]),
698 Double.parseDouble(fields[40]),
699 Double.parseDouble(fields[41])))));
700 }
701
702 },
703
704
705 DEM_TILE() {
706
707
708 @Override
709 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
710 if (fields.length < 13 ||
711 !fields[1].equals(LAT_MIN) || !fields[3].equals(LAT_STEP) || !fields[5].equals(LAT_ROWS) ||
712 !fields[7].equals(LON_MIN) || !fields[9].equals(LON_STEP) || !fields[11].equals(LON_COLS)) {
713 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
714 }
715 final String name = fields[0];
716 final double minLatitude = Double.parseDouble(fields[2]);
717 final double latitudeStep = Double.parseDouble(fields[4]);
718 final int latitudeRows = Integer.parseInt(fields[6]);
719 final double minLongitude = Double.parseDouble(fields[8]);
720 final double longitudeStep = Double.parseDouble(fields[10]);
721 final int longitudeColumns = Integer.parseInt(fields[12]);
722 for (final ParsedTile tile : global.tiles) {
723 if (tile.name.equals(name)) {
724 throw new RuggedException(RuggedMessages.TILE_ALREADY_DEFINED,
725 name, l, file.getAbsolutePath(), line);
726 }
727 }
728 global.tiles.add(new ParsedTile(name,
729 minLatitude, latitudeStep, latitudeRows,
730 minLongitude, longitudeStep, longitudeColumns));
731 }
732
733 },
734
735
736 DEM_CELL() {
737
738
739 @Override
740 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
741 if (fields.length < 7 ||
742 !fields[1].equals(LAT_INDEX) || !fields[3].equals(LON_INDEX) || !fields[5].equals(ELEVATION)) {
743 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
744 }
745 final String name = fields[0];
746 final int latIndex = Integer.parseInt(fields[2]);
747 final int lonIndex = Integer.parseInt(fields[4]);
748 final double elevation = Double.parseDouble(fields[6]);
749 for (final ParsedTile tile : global.tiles) {
750 if (tile.name.equals(name)) {
751 final int index = latIndex * tile.longitudeColumns + lonIndex;
752 tile.elevations.put(index, elevation);
753 return;
754 }
755 }
756 throw new RuggedException(RuggedMessages.UNKNOWN_TILE,
757 name, l, file.getAbsolutePath(), line);
758 }
759
760 },
761
762
763 INVERSE_LOCATION() {
764
765
766 @Override
767 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
768 if (fields.length < 18 ||
769 !fields[0].equals(SENSOR_NAME) ||
770 !fields[2].equals(LATITUDE) || !fields[4].equals(LONGITUDE) || !fields[6].equals(ELEVATION) ||
771 !fields[8].equals(MIN_LINE) || !fields[10].equals(MAX_LINE) ||
772 !fields[12].equals(LIGHT_TIME) || !fields[14].equals(ABERRATION) ||
773 !fields[16].equals(REFRACTION)) {
774 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
775 }
776 final String sensorName = fields[1];
777 final GeodeticPoint point = new GeodeticPoint(Double.parseDouble(fields[3]),
778 Double.parseDouble(fields[5]),
779 Double.parseDouble(fields[7]));
780 final int minLine = Integer.parseInt(fields[9]);
781 final int maxLine = Integer.parseInt(fields[11]);
782 if (global.calls.isEmpty()) {
783 global.lightTimeCorrection = Boolean.parseBoolean(fields[13]);
784 global.aberrationOfLightCorrection = Boolean.parseBoolean(fields[15]);
785 global.atmosphericRefraction = Boolean.parseBoolean(fields[17]);
786 } else {
787 if (global.lightTimeCorrection != Boolean.parseBoolean(fields[13])) {
788 throw new RuggedException(RuggedMessages.LIGHT_TIME_CORRECTION_REDEFINED,
789 l, file.getAbsolutePath(), line);
790 }
791 if (global.aberrationOfLightCorrection != Boolean.parseBoolean(fields[15])) {
792 throw new RuggedException(RuggedMessages.ABERRATION_OF_LIGHT_CORRECTION_REDEFINED,
793 l, file.getAbsolutePath(), line);
794 }
795 if (global.atmosphericRefraction != Boolean.parseBoolean(fields[17])) {
796 throw new RuggedException(RuggedMessages.ATMOSPHERIC_REFRACTION_REDEFINED,
797 l, file.getAbsolutePath(), line);
798 }
799 }
800 global.calls.add(new DumpedCall() {
801
802
803 @Override
804 public Object execute(final Rugged rugged) {
805 return rugged.inverseLocation(sensorName, point, minLine, maxLine);
806 }
807
808 });
809 }
810
811 },
812
813
814 INVERSE_LOCATION_RESULT() {
815
816
817 @Override
818 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
819 if (fields.length == 1) {
820 if (fields[0].equals(NULL_RESULT)) {
821 final SensorPixel sp = null;
822 final DumpedCall last = global.calls.get(global.calls.size() - 1);
823 last.expected = sp;
824 } else {
825 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
826 }
827 } else if (fields.length < 4 || !fields[0].equals(LINE_NUMBER) || !fields[2].equals(PIXEL_NUMBER)) {
828 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
829 } else {
830 final SensorPixel sp = new SensorPixel(Double.parseDouble(fields[1]),
831 Double.parseDouble(fields[3]));
832 final DumpedCall last = global.calls.get(global.calls.size() - 1);
833 last.expected = sp;
834 }
835 }
836
837 },
838
839
840 SENSOR() {
841
842
843 @Override
844 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
845 if (fields.length < 8 || !fields[0].equals(SENSOR_NAME) ||
846 !fields[2].equals(NB_PIXELS) || !fields[4].equals(POSITION)) {
847 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
848 }
849 final ParsedSensor sensor = global.getSensor(fields[1]);
850 sensor.setNbPixels(Integer.parseInt(fields[3]));
851 sensor.setPosition(new Vector3D(Double.parseDouble(fields[5]),
852 Double.parseDouble(fields[6]),
853 Double.parseDouble(fields[7])));
854 }
855
856 },
857
858
859 SENSOR_MEAN_PLANE() {
860
861
862 @Override
863 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
864 if (fields.length < 16 || !fields[0].equals(SENSOR_NAME) ||
865 !fields[2].equals(MIN_LINE) || !fields[4].equals(MAX_LINE) ||
866 !fields[6].equals(MAX_EVAL) || !fields[8].equals(ACCURACY) ||
867 !fields[10].equals(NORMAL) || !fields[14].equals(CACHED_RESULTS)) {
868 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
869 }
870 final String sensorName = fields[1];
871 final int minLine = Integer.parseInt(fields[3]);
872 final int maxLine = Integer.parseInt(fields[5]);
873 final int maxEval = Integer.parseInt(fields[7]);
874 final double accuracy = Double.parseDouble(fields[9]);
875 final Vector3D normal = new Vector3D(Double.parseDouble(fields[11]),
876 Double.parseDouble(fields[12]),
877 Double.parseDouble(fields[13]));
878 final int n = Integer.parseInt(fields[15]);
879 final CrossingResult[] cachedResults = new CrossingResult[n];
880 int base = 16;
881 for (int i = 0; i < n; ++i) {
882 if (fields.length < base + 15 || !fields[base].equals(LINE_NUMBER) ||
883 !fields[base + 2].equals(DATE) || !fields[base + 4].equals(TARGET) ||
884 !fields[base + 8].equals(TARGET_DIRECTION)) {
885 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
886 }
887 final double ln = Double.parseDouble(fields[base + 1]);
888 final AbsoluteDate date = new AbsoluteDate(fields[base + 3], TimeScalesFactory.getUTC());
889 final Vector3D target = new Vector3D(Double.parseDouble(fields[base + 5]),
890 Double.parseDouble(fields[base + 6]),
891 Double.parseDouble(fields[base + 7]));
892 final Vector3D targetDirection = new Vector3D(Double.parseDouble(fields[base + 9]),
893 Double.parseDouble(fields[base + 10]),
894 Double.parseDouble(fields[base + 11]));
895 final Vector3D targetDirectionDerivative = new Vector3D(Double.parseDouble(fields[base + 12]),
896 Double.parseDouble(fields[base + 13]),
897 Double.parseDouble(fields[base + 14]));
898 cachedResults[i] = new CrossingResult(date, ln, target, targetDirection, targetDirectionDerivative);
899 base += 15;
900 }
901 global.getSensor(sensorName).setMeanPlane(new ParsedMeanPlane(minLine, maxLine, maxEval, accuracy, normal, cachedResults));
902 }
903 },
904
905
906 SENSOR_LOS() {
907
908
909 @Override
910 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
911 if (fields.length < 10 || !fields[0].equals(SENSOR_NAME) ||
912 !fields[2].equals(DATE) || !fields[4].equals(PIXEL_NUMBER) ||
913 !fields[6].equals(LOS)) {
914 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
915 }
916 final String sensorName = fields[1];
917 final AbsoluteDate date = new AbsoluteDate(fields[3], TimeScalesFactory.getUTC());
918 final int pixelNumber = Integer.parseInt(fields[5]);
919 final Vector3D los = new Vector3D(Double.parseDouble(fields[7]),
920 Double.parseDouble(fields[8]),
921 Double.parseDouble(fields[9]));
922 global.getSensor(sensorName).setLOS(date, pixelNumber, los);
923 }
924 },
925
926
927 SENSOR_DATATION() {
928
929
930 @Override
931 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
932 if (fields.length < 6 || !fields[0].equals(SENSOR_NAME) ||
933 !fields[2].equals(LINE_NUMBER) || !fields[4].equals(DATE)) {
934 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
935 }
936 final String sensorName = fields[1];
937 final double lineNumber = Double.parseDouble(fields[3]);
938 final AbsoluteDate date = new AbsoluteDate(fields[5], TimeScalesFactory.getUTC());
939 global.getSensor(sensorName).setDatation(lineNumber, date);
940 }
941 },
942
943
944 SENSOR_RATE() {
945
946
947 @Override
948 public void parse(final int l, final File file, final String line, final String[] fields, final DumpReplayer global) {
949 if (fields.length < 6 || !fields[0].equals(SENSOR_NAME) ||
950 !fields[2].equals(LINE_NUMBER) || !fields[4].equals(RATE)) {
951 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
952 }
953 final String sensorName = fields[1];
954 final double lineNumber = Double.parseDouble(fields[3]);
955 final double rate = Double.parseDouble(fields[5]);
956 global.getSensor(sensorName).setRate(lineNumber, rate);
957
958 }
959
960 };
961
962
963
964
965
966
967
968 public static void parse(final int l, final File file, final String line, final DumpReplayer global) {
969
970 final String trimmed = line.trim();
971 if (trimmed.isEmpty() || trimmed.startsWith(COMMENT_START)) {
972 return;
973 }
974
975 final int colon = line.indexOf(':');
976 if (colon > 0) {
977 final String parsedKey = PATTERN.matcher(line.substring(0, colon).trim()).replaceAll("_").toUpperCase();
978 try {
979 final LineParser parser = LineParser.valueOf(parsedKey);
980 final String[] fields;
981 if (colon + 1 >= line.length()) {
982 fields = new String[0];
983 } else {
984 fields = SEPARATOR.split(line.substring(colon + 1).trim());
985 }
986 parser.parse(l, file, line, fields, global);
987 } catch (IllegalArgumentException iae) {
988 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
989 }
990
991 } else {
992 throw new RuggedException(RuggedMessages.CANNOT_PARSE_LINE, l, file, line);
993 }
994
995 }
996
997
998
999
1000
1001
1002
1003
1004 public abstract void parse(int l, File file, String line, String[] fields, DumpReplayer global);
1005
1006 }
1007
1008
1009 private static class ParsedTile {
1010
1011
1012 private final String name;
1013
1014
1015 private final double minLatitude;
1016
1017
1018 private final double latitudeStep;
1019
1020
1021 private final int latitudeRows;
1022
1023
1024 private final double minLongitude;
1025
1026
1027 private final double longitudeStep;
1028
1029
1030 private final int longitudeColumns;
1031
1032
1033 private final OpenIntToDoubleHashMap elevations;
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 ParsedTile(final String name,
1045 final double minLatitude, final double latitudeStep, final int latitudeRows,
1046 final double minLongitude, final double longitudeStep, final int longitudeColumns) {
1047 this.name = name;
1048 this.minLatitude = minLatitude;
1049 this.latitudeStep = latitudeStep;
1050 this.minLongitude = minLongitude;
1051 this.longitudeStep = longitudeStep;
1052 this.latitudeRows = latitudeRows;
1053 this.longitudeColumns = longitudeColumns;
1054 this.elevations = new OpenIntToDoubleHashMap();
1055 }
1056
1057
1058
1059
1060
1061
1062 public boolean isInterpolable(final double latitude, final double longitude) {
1063 final int latitudeIndex = (int) FastMath.floor((latitude - minLatitude) / latitudeStep);
1064 final int longitudeIndex = (int) FastMath.floor((longitude - minLongitude) / longitudeStep);
1065 return latitudeIndex >= 0 && latitudeIndex <= latitudeRows - 2 &&
1066 longitudeIndex >= 0 && longitudeIndex <= longitudeColumns - 2;
1067 }
1068
1069
1070
1071
1072 public void updateTile(final UpdatableTile tile) {
1073
1074 tile.setGeometry(minLatitude, minLongitude,
1075 latitudeStep, longitudeStep,
1076 latitudeRows, longitudeColumns);
1077
1078 final OpenIntToDoubleHashMap.Iterator iterator = elevations.iterator();
1079 while (iterator.hasNext()) {
1080 iterator.advance();
1081 final int index = iterator.key();
1082 final int latitudeIndex = index / longitudeColumns;
1083 final int longitudeIndex = index % longitudeColumns;
1084 final double elevation = iterator.value();
1085 tile.setElevation(latitudeIndex, longitudeIndex, elevation);
1086 }
1087
1088 }
1089
1090 }
1091
1092
1093 private static class ParsedSensor implements LineDatation, TimeDependentLOS {
1094
1095
1096 private final String name;
1097
1098
1099 private int nbPixels;
1100
1101
1102 private Vector3D position;
1103
1104
1105 private ParsedMeanPlane meanPlane;
1106
1107
1108 private final Map<Integer, List<Pair<AbsoluteDate, Vector3D>>> losMap;
1109
1110
1111 private final List<Pair<Double, AbsoluteDate>> datation;
1112
1113
1114 private final List<Pair<Double, Double>> rates;
1115
1116
1117
1118
1119 ParsedSensor(final String name) {
1120 this.name = name;
1121 this.losMap = new HashMap<>();
1122 this.datation = new ArrayList<>();
1123 this.rates = new ArrayList<>();
1124 }
1125
1126
1127
1128
1129 public void setMeanPlane(final ParsedMeanPlane meanPlane) {
1130 this.meanPlane = meanPlane;
1131 }
1132
1133
1134
1135
1136 public void setPosition(final Vector3D position) {
1137 this.position = position;
1138 }
1139
1140
1141
1142
1143 public void setNbPixels(final int nbPixels) {
1144 this.nbPixels = nbPixels;
1145 }
1146
1147
1148 @Override
1149 public int getNbPixels() {
1150 return nbPixels;
1151 }
1152
1153
1154
1155
1156
1157
1158 public void setLOS(final AbsoluteDate date, final int pixelNumber, final Vector3D los) {
1159 List<Pair<AbsoluteDate, Vector3D>> list = losMap.get(pixelNumber);
1160 if (list == null) {
1161 list = new ArrayList<>();
1162 losMap.put(pixelNumber, list);
1163 }
1164
1165 int index = 0;
1166 while (index < list.size()) {
1167 if (list.get(index).getFirst().compareTo(date) > 0) {
1168 break;
1169 }
1170 ++index;
1171 }
1172 list.add(index, new Pair<>(date, los));
1173 }
1174
1175
1176 @Override
1177 public Vector3D getLOS(final int index, final AbsoluteDate date) {
1178 final List<Pair<AbsoluteDate, Vector3D>> list = losMap.get(index);
1179 if (list == null) {
1180 throw new RuggedInternalError(null);
1181 }
1182
1183 if (list.size() < 2) {
1184 return list.get(0).getSecond();
1185 }
1186
1187
1188 int sup = 0;
1189 while (sup < list.size() - 1) {
1190 if (list.get(sup).getFirst().compareTo(date) >= 0) {
1191 break;
1192 }
1193 ++sup;
1194 }
1195 final int inf = (sup == 0) ? sup++ : (sup - 1);
1196
1197 final AbsoluteDate dInf = list.get(inf).getFirst();
1198 final Vector3D lInf = list.get(inf).getSecond();
1199 final AbsoluteDate dSup = list.get(sup).getFirst();
1200 final Vector3D lSup = list.get(sup).getSecond();
1201 final double alpha = date.durationFrom(dInf) / dSup.durationFrom(dInf);
1202 return new Vector3D(alpha, lSup, 1 - alpha, lInf);
1203
1204 }
1205
1206
1207 @Override
1208 public <T extends Derivative<T>> FieldVector3D<T> getLOSDerivatives(final int index, final AbsoluteDate date,
1209 final DerivativeGenerator<T> generator) {
1210 final Vector3D los = getLOS(index, date);
1211 return new FieldVector3D<>(generator.constant(los.getX()),
1212 generator.constant(los.getY()),
1213 generator.constant(los.getZ()));
1214 }
1215
1216
1217
1218
1219
1220 public void setDatation(final double lineNumber, final AbsoluteDate date) {
1221
1222 int index = 0;
1223 while (index < datation.size()) {
1224 if (datation.get(index).getSecond().compareTo(date) > 0) {
1225 break;
1226 }
1227 ++index;
1228 }
1229 datation.add(index, new Pair<>(lineNumber, date));
1230 }
1231
1232
1233 @Override
1234 public AbsoluteDate getDate(final double lineNumber) {
1235
1236 if (datation.size() < 2) {
1237 return datation.get(0).getSecond();
1238 }
1239
1240
1241 int sup = 0;
1242 while (sup < datation.size() - 1) {
1243 if (datation.get(sup).getFirst() >= lineNumber) {
1244 break;
1245 }
1246 ++sup;
1247 }
1248 final int inf = (sup == 0) ? sup++ : (sup - 1);
1249
1250 final double lInf = datation.get(inf).getFirst();
1251 final AbsoluteDate dInf = datation.get(inf).getSecond();
1252 final double lSup = datation.get(sup).getFirst();
1253 final AbsoluteDate dSup = datation.get(sup).getSecond();
1254 final double alpha = (lineNumber - lInf) / (lSup - lInf);
1255 return dInf.shiftedBy(alpha * dSup.durationFrom(dInf));
1256
1257 }
1258
1259
1260 @Override
1261 public double getLine(final AbsoluteDate date) {
1262
1263 if (datation.size() < 2) {
1264 return datation.get(0).getFirst();
1265 }
1266
1267
1268 int sup = 0;
1269 while (sup < datation.size() - 1) {
1270 if (datation.get(sup).getSecond().compareTo(date) >= 0) {
1271 break;
1272 }
1273 ++sup;
1274 }
1275 final int inf = (sup == 0) ? sup++ : (sup - 1);
1276
1277 final double lInf = datation.get(inf).getFirst();
1278 final AbsoluteDate dInf = datation.get(inf).getSecond();
1279 final double lSup = datation.get(sup).getFirst();
1280 final AbsoluteDate dSup = datation.get(sup).getSecond();
1281 final double alpha = date.durationFrom(dInf) / dSup.durationFrom(dInf);
1282 return alpha * lSup + (1 - alpha) * lInf;
1283
1284 }
1285
1286
1287
1288
1289
1290 public void setRate(final double lineNumber, final double rate) {
1291
1292 int index = 0;
1293 while (index < rates.size()) {
1294 if (rates.get(index).getFirst() > lineNumber) {
1295 break;
1296 }
1297 ++index;
1298 }
1299 rates.add(index, new Pair<>(lineNumber, rate));
1300 }
1301
1302
1303 @Override
1304 public double getRate(final double lineNumber) {
1305
1306 if (rates.size() < 2) {
1307 return rates.get(0).getSecond();
1308 }
1309
1310
1311 int sup = 0;
1312 while (sup < rates.size() - 1) {
1313 if (rates.get(sup).getFirst() >= lineNumber) {
1314 break;
1315 }
1316 ++sup;
1317 }
1318 final int inf = (sup == 0) ? sup++ : (sup - 1);
1319
1320 final double lInf = rates.get(inf).getFirst();
1321 final double rInf = rates.get(inf).getSecond();
1322 final double lSup = rates.get(sup).getFirst();
1323 final double rSup = rates.get(sup).getSecond();
1324 final double alpha = (lineNumber - lInf) / (lSup - lInf);
1325 return alpha * rSup + (1 - alpha) * rInf;
1326
1327 }
1328
1329
1330 @Override
1331 public Stream<ParameterDriver> getParametersDrivers() {
1332 return Stream.<ParameterDriver>empty();
1333 }
1334
1335 }
1336
1337
1338 private static class ParsedMeanPlane {
1339
1340
1341 private final int minLine;
1342
1343
1344 private final int maxLine;
1345
1346
1347 private final int maxEval;
1348
1349
1350 private final double accuracy;
1351
1352
1353 private final Vector3D normal;
1354
1355
1356 private final CrossingResult[] cachedResults;
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366 ParsedMeanPlane(final int minLine, final int maxLine,
1367 final int maxEval, final double accuracy, final Vector3D normal,
1368 final CrossingResult[] cachedResults) {
1369 this.minLine = minLine;
1370 this.maxLine = maxLine;
1371 this.maxEval = maxEval;
1372 this.accuracy = accuracy;
1373 this.normal = normal;
1374 this.cachedResults = cachedResults.clone();
1375 }
1376
1377 }
1378
1379
1380 private abstract static class DumpedCall {
1381
1382
1383 private Object expected;
1384
1385
1386
1387
1388
1389 public abstract Object execute(Rugged rugged);
1390
1391 }
1392
1393 }