1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.frames;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.nio.charset.StandardCharsets;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.SortedSet;
27 import java.util.function.Supplier;
28
29 import javax.xml.parsers.ParserConfigurationException;
30 import javax.xml.parsers.SAXParser;
31 import javax.xml.parsers.SAXParserFactory;
32
33 import org.hipparchus.exception.LocalizedCoreFormats;
34 import org.orekit.data.DataProvidersManager;
35 import org.orekit.errors.OrekitException;
36 import org.orekit.errors.OrekitMessages;
37 import org.orekit.time.AbsoluteDate;
38 import org.orekit.time.DateComponents;
39 import org.orekit.time.TimeScale;
40 import org.orekit.utils.IERSConventions;
41 import org.orekit.utils.units.Unit;
42 import org.xml.sax.Attributes;
43 import org.xml.sax.InputSource;
44 import org.xml.sax.SAXException;
45 import org.xml.sax.helpers.DefaultHandler;
46
47
48
49
50
51
52
53
54
55
56
57
58
59 class EopXmlLoader extends AbstractEopLoader implements EopHistoryLoader {
60
61
62 private static final Unit MILLI_SECOND = Unit.parse("ms");
63
64
65 private static final Unit MILLI_ARC_SECOND = Unit.parse("mas");
66
67
68
69
70 private static final Unit ARC_SECOND_PER_DAY = Unit.parse("as/day");
71
72
73
74
75
76
77
78
79 EopXmlLoader(final String supportedNames,
80 final DataProvidersManager manager,
81 final Supplier<TimeScale> utcSupplier) {
82 super(supportedNames, manager, utcSupplier);
83 }
84
85
86 public void fillHistory(final IERSConventions.NutationCorrectionConverter converter,
87 final SortedSet<EOPEntry> history) {
88 final ItrfVersionProvider itrfVersionProvider = new ITRFVersionLoader(
89 ITRFVersionLoader.SUPPORTED_NAMES,
90 getDataProvidersManager());
91 final Parser parser = new Parser(converter, itrfVersionProvider, getUtc());
92 final EopParserLoader loader = new EopParserLoader(parser);
93 this.feed(loader);
94 history.addAll(loader.getEop());
95 }
96
97
98 static class Parser extends AbstractEopParser {
99
100
101 private List<EOPEntry> history;
102
103
104
105
106
107
108
109
110 Parser(final IERSConventions.NutationCorrectionConverter converter,
111 final ItrfVersionProvider itrfVersionProvider,
112 final TimeScale utc) {
113 super(converter, itrfVersionProvider, utc);
114 }
115
116
117 @Override
118 public Collection<EOPEntry> parse(final InputStream input, final String name)
119 throws IOException, OrekitException {
120 try {
121 this.history = new ArrayList<>();
122
123 final SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
124
125
126 parser.parse(new InputSource(new InputStreamReader(input, StandardCharsets.UTF_8)),
127 new EOPContentHandler(name));
128
129 return history;
130
131 } catch (SAXException | ParserConfigurationException e) {
132 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getMessage());
133 }
134 }
135
136
137 private class EOPContentHandler extends DefaultHandler {
138
139
140
141
142 private static final String MJD_ELT = "MJD";
143 private static final String LOD_ELT = "LOD";
144 private static final String X_ELT = "X";
145 private static final String Y_ELT = "Y";
146 private static final String X_RATE_ELT = "x_rate";
147 private static final String Y_RATE_ELT = "y_rate";
148 private static final String DPSI_ELT = "dPsi";
149 private static final String DEPSILON_ELT = "dEpsilon";
150 private static final String DX_ELT = "dX";
151 private static final String DY_ELT = "dY";
152
153
154 private static final String DATA_ELT = "data";
155 private static final String PRODUCT_ATTR = "product";
156 private static final String BULLETIN_A_PROD = "BulletinA";
157 private static final String BULLETIN_B_PROD = "BulletinB";
158 private static final String EOP_C04_PROD_PREFIX = "EOP";
159 private static final String EOP_C04_PROD_SUFFIX = "C04";
160
161
162 private static final String DATA_EOP_ELT = "dataEOP";
163 private static final String TIME_SERIES_ELT = "timeSeries";
164 private static final String DATE_YEAR_ELT = "dateYear";
165 private static final String DATE_MONTH_ELT = "dateMonth";
166 private static final String DATE_DAY_ELT = "dateDay";
167 private static final String POLE_ELT = "pole";
168 private static final String UT_ELT = "UT";
169 private static final String UT1_U_UTC_ELT = "UT1_UTC";
170 private static final String NUTATION_ELT = "nutation";
171 private static final String SOURCE_ATTR = "source";
172
173
174 private static final String FINALS_ELT = "Finals";
175 private static final String DATE_ELT = "date";
176 private static final String EOP_SET_ELT = "EOPSet";
177 private static final String BULLETIN_A_ELT = "bulletinA";
178 private static final String UT1_M_UTC_ELT = "UT1-UTC";
179
180 private boolean inBulletinA;
181 private int year;
182 private int month;
183 private int day;
184 private int mjd;
185 private AbsoluteDate mjdDate;
186 private double dtu1;
187 private double lod;
188 private double x;
189 private double y;
190 private double xRate;
191 private double yRate;
192 private double dpsi;
193 private double deps;
194 private double dx;
195 private double dy;
196
197
198
199
200 private final String name;
201
202
203 private final StringBuilder buffer;
204
205
206 private DataFileContent content;
207
208
209 private ITRFVersionLoader.ITRFVersionConfiguration configuration;
210
211
212
213
214 EOPContentHandler(final String name) {
215 this.name = name;
216 this.buffer = new StringBuilder();
217 }
218
219
220 @Override
221 public void startDocument() {
222 content = DataFileContent.UNKNOWN;
223 configuration = null;
224 }
225
226
227 @Override
228 public void characters(final char[] ch, final int start, final int length) {
229 buffer.append(ch, start, length);
230 }
231
232
233 @Override
234 public void startElement(final String uri, final String localName,
235 final String qName, final Attributes atts) {
236
237
238 buffer.delete(0, buffer.length());
239
240 if (content == DataFileContent.UNKNOWN) {
241
242 if (qName.equals(TIME_SERIES_ELT)) {
243
244 content = DataFileContent.DAILY;
245 } else if (qName.equals(FINALS_ELT)) {
246
247 content = DataFileContent.FINAL;
248 } else if (qName.equals(DATA_ELT)) {
249 final String product = atts.getValue(PRODUCT_ATTR);
250 if (product != null) {
251 if (product.startsWith(BULLETIN_A_PROD)) {
252
253 content = DataFileContent.BULLETIN_A;
254 inBulletinA = true;
255 } else if (product.startsWith(BULLETIN_B_PROD)) {
256
257 content = DataFileContent.BULLETIN_B;
258 } else if (product.startsWith(EOP_C04_PROD_PREFIX) && product.endsWith(EOP_C04_PROD_SUFFIX)) {
259
260 content = DataFileContent.EOP_C04;
261 }
262 }
263 }
264 }
265
266 if (content == DataFileContent.DAILY || content == DataFileContent.BULLETIN_A ||
267 content == DataFileContent.BULLETIN_B || content == DataFileContent.EOP_C04) {
268 startDailyElement(qName, atts);
269 } else if (content == DataFileContent.FINAL) {
270 startFinalElement(qName);
271 }
272
273 }
274
275
276
277
278
279 private void startDailyElement(final String qName, final Attributes atts) {
280 if (qName.equals(TIME_SERIES_ELT)) {
281
282 resetEOPData();
283 } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) {
284 final String source = atts.getValue(SOURCE_ATTR);
285 if (source != null) {
286 inBulletinA = source.equals(BULLETIN_A_PROD);
287 }
288 }
289 }
290
291
292
293
294 private void startFinalElement(final String qName) {
295 if (qName.equals(EOP_SET_ELT)) {
296
297 resetEOPData();
298 } else if (qName.equals(BULLETIN_A_ELT)) {
299 inBulletinA = true;
300 }
301 }
302
303
304
305 private void resetEOPData() {
306 inBulletinA = false;
307 year = -1;
308 month = -1;
309 day = -1;
310 mjd = -1;
311 mjdDate = null;
312 dtu1 = Double.NaN;
313 lod = Double.NaN;
314 x = Double.NaN;
315 y = Double.NaN;
316 xRate = Double.NaN;
317 yRate = Double.NaN;
318 dpsi = Double.NaN;
319 deps = Double.NaN;
320 dx = Double.NaN;
321 dy = Double.NaN;
322 }
323
324
325 @Override
326 public void endElement(final String uri, final String localName, final String qName) {
327 if (content == DataFileContent.DAILY || content == DataFileContent.BULLETIN_A ||
328 content == DataFileContent.BULLETIN_B || content == DataFileContent.EOP_C04) {
329 endDailyElement(qName);
330 } else if (content == DataFileContent.FINAL) {
331 endFinalElement(qName);
332 }
333 }
334
335
336
337
338 private void endDailyElement(final String qName) {
339 if (qName.equals(DATE_YEAR_ELT) && buffer.length() > 0) {
340 year = Integer.parseInt(buffer.toString());
341 } else if (qName.equals(DATE_MONTH_ELT) && buffer.length() > 0) {
342 month = Integer.parseInt(buffer.toString());
343 } else if (qName.equals(DATE_DAY_ELT) && buffer.length() > 0) {
344 day = Integer.parseInt(buffer.toString());
345 } else if (qName.equals(MJD_ELT) && buffer.length() > 0) {
346 mjd = Integer.parseInt(buffer.toString());
347 mjdDate = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
348 getUtc());
349 } else if (qName.equals(UT1_M_UTC_ELT)) {
350 dtu1 = overwrite(dtu1, Unit.SECOND);
351 } else if (qName.equals(LOD_ELT)) {
352 lod = overwrite(lod, MILLI_SECOND);
353 } else if (qName.equals(X_ELT)) {
354 x = overwrite(x, Unit.ARC_SECOND);
355 } else if (qName.equals(Y_ELT)) {
356 y = overwrite(y, Unit.ARC_SECOND);
357 } else if (qName.equals(X_RATE_ELT)) {
358 xRate = overwrite(xRate, ARC_SECOND_PER_DAY);
359 } else if (qName.equals(Y_RATE_ELT)) {
360 yRate = overwrite(yRate, ARC_SECOND_PER_DAY);
361 } else if (qName.equals(DPSI_ELT)) {
362 dpsi = overwrite(dpsi, MILLI_ARC_SECOND);
363 } else if (qName.equals(DEPSILON_ELT)) {
364 deps = overwrite(deps, MILLI_ARC_SECOND);
365 } else if (qName.equals(DX_ELT)) {
366 dx = overwrite(dx, MILLI_ARC_SECOND);
367 } else if (qName.equals(DY_ELT)) {
368 dy = overwrite(dy, MILLI_ARC_SECOND);
369 } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) {
370 inBulletinA = false;
371 } else if (qName.equals(DATA_EOP_ELT)) {
372 checkDates();
373 if (!Double.isNaN(dtu1) && !Double.isNaN(x) && !Double.isNaN(y)) {
374 final double[] equinox;
375 final double[] nro;
376 if (Double.isNaN(dpsi)) {
377 nro = new double[] {
378 dx, dy
379 };
380 equinox = getConverter().toEquinox(mjdDate, nro[0], nro[1]);
381 } else {
382 equinox = new double[] {
383 dpsi, deps
384 };
385 nro = getConverter().toNonRotating(mjdDate, equinox[0], equinox[1]);
386 }
387 if (configuration == null || !configuration.isValid(mjd)) {
388
389 configuration = getItrfVersionProvider().getConfiguration(name, mjd);
390 }
391 history.add(new EOPEntry(mjd, dtu1, lod, x, y, Double.NaN, Double.NaN,
392 equinox[0], equinox[1], nro[0], nro[1],
393 configuration.getVersion(), mjdDate));
394 }
395 }
396 }
397
398
399
400
401 private void endFinalElement(final String qName) {
402 if (qName.equals(DATE_ELT) && buffer.length() > 0) {
403 final String[] fields = buffer.toString().split("-");
404 if (fields.length == 3) {
405 year = Integer.parseInt(fields[0]);
406 month = Integer.parseInt(fields[1]);
407 day = Integer.parseInt(fields[2]);
408 }
409 } else if (qName.equals(MJD_ELT) && buffer.length() > 0) {
410 mjd = Integer.parseInt(buffer.toString());
411 mjdDate = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
412 getUtc());
413 } else if (qName.equals(UT1_U_UTC_ELT)) {
414 dtu1 = overwrite(dtu1, Unit.SECOND);
415 } else if (qName.equals(LOD_ELT)) {
416 lod = overwrite(lod, MILLI_SECOND);
417 } else if (qName.equals(X_ELT)) {
418 x = overwrite(x, Unit.ARC_SECOND);
419 } else if (qName.equals(Y_ELT)) {
420 y = overwrite(y, Unit.ARC_SECOND);
421 } else if (qName.equals(X_RATE_ELT)) {
422 xRate = overwrite(xRate, ARC_SECOND_PER_DAY);
423 } else if (qName.equals(Y_RATE_ELT)) {
424 yRate = overwrite(yRate, ARC_SECOND_PER_DAY);
425 } else if (qName.equals(DPSI_ELT)) {
426 dpsi = overwrite(dpsi, MILLI_ARC_SECOND);
427 } else if (qName.equals(DEPSILON_ELT)) {
428 deps = overwrite(deps, MILLI_ARC_SECOND);
429 } else if (qName.equals(DX_ELT)) {
430 dx = overwrite(dx, MILLI_ARC_SECOND);
431 } else if (qName.equals(DY_ELT)) {
432 dy = overwrite(dy, MILLI_ARC_SECOND);
433 } else if (qName.equals(BULLETIN_A_ELT)) {
434 inBulletinA = false;
435 } else if (qName.equals(EOP_SET_ELT)) {
436 checkDates();
437 if (!Double.isNaN(dtu1) && !Double.isNaN(x) && !Double.isNaN(y)) {
438 final double[] equinox;
439 final double[] nro;
440 if (Double.isNaN(dpsi)) {
441 nro = new double[] {
442 dx, dy
443 };
444 equinox = getConverter().toEquinox(mjdDate, nro[0], nro[1]);
445 } else {
446 equinox = new double[] {
447 dpsi, deps
448 };
449 nro = getConverter().toNonRotating(mjdDate, equinox[0], equinox[1]);
450 }
451 if (configuration == null || !configuration.isValid(mjd)) {
452
453 configuration = getItrfVersionProvider().getConfiguration(name, mjd);
454 }
455 history.add(new EOPEntry(mjd, dtu1, lod, x, y, xRate, yRate,
456 equinox[0], equinox[1], nro[0], nro[1],
457 configuration.getVersion(), mjdDate));
458 }
459 }
460 }
461
462
463
464
465
466
467 private double overwrite(final double oldValue, final Unit units) {
468 if (buffer.length() == 0) {
469
470 return oldValue;
471 } else if (inBulletinA && !Double.isNaN(oldValue)) {
472
473 return oldValue;
474 } else {
475
476 return units.toSI(Double.parseDouble(buffer.toString()));
477 }
478 }
479
480
481
482 private void checkDates() {
483 if (new DateComponents(year, month, day).getMJD() != mjd) {
484 throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
485 name, year, month, day, mjd);
486 }
487 }
488
489
490 @Override
491 public InputSource resolveEntity(final String publicId, final String systemId) {
492
493 return new InputSource();
494 }
495
496 }
497
498 }
499
500
501 private enum DataFileContent {
502
503
504 UNKNOWN,
505
506
507
508
509 BULLETIN_A,
510
511
512
513
514 BULLETIN_B,
515
516
517
518
519 EOP_C04,
520
521
522 DAILY,
523
524
525 FINAL
526
527 }
528
529 }