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.SAXParserFactory;
31
32 import org.hipparchus.exception.LocalizedCoreFormats;
33 import org.orekit.data.DataProvidersManager;
34 import org.orekit.errors.OrekitException;
35 import org.orekit.errors.OrekitMessages;
36 import org.orekit.time.AbsoluteDate;
37 import org.orekit.time.DateComponents;
38 import org.orekit.time.TimeScale;
39 import org.orekit.utils.Constants;
40 import org.orekit.utils.IERSConventions;
41 import org.xml.sax.Attributes;
42 import org.xml.sax.InputSource;
43 import org.xml.sax.SAXException;
44 import org.xml.sax.XMLReader;
45 import org.xml.sax.helpers.DefaultHandler;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 class RapidDataAndPredictionXMLLoader extends AbstractEopLoader
64 implements EOPHistoryLoader {
65
66
67 private static final double MILLI_ARC_SECONDS_TO_RADIANS = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
68
69
70 private static final double MILLI_SECONDS_TO_SECONDS = 1.0 / 1000.0;
71
72
73
74
75
76
77
78
79 RapidDataAndPredictionXMLLoader(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 EopParserLoaderopParserLoader">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 XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
124 reader.setContentHandler(new EOPContentHandler(name));
125
126 reader.setEntityResolver((publicId, systemId) -> new InputSource());
127
128
129 reader.parse(new InputSource(new InputStreamReader(input, StandardCharsets.UTF_8)));
130
131 return history;
132
133 } catch (SAXException se) {
134 if ((se.getCause() != null) && (se.getCause() instanceof OrekitException)) {
135 throw (OrekitException) se.getCause();
136 }
137 throw new OrekitException(se, LocalizedCoreFormats.SIMPLE_MESSAGE, se.getMessage());
138 } catch (ParserConfigurationException pce) {
139 throw new OrekitException(pce, LocalizedCoreFormats.SIMPLE_MESSAGE, pce.getMessage());
140 }
141 }
142
143
144 private class EOPContentHandler extends DefaultHandler {
145
146
147
148
149 private static final String MJD_ELT = "MJD";
150 private static final String LOD_ELT = "LOD";
151 private static final String X_ELT = "X";
152 private static final String Y_ELT = "Y";
153 private static final String DPSI_ELT = "dPsi";
154 private static final String DEPSILON_ELT = "dEpsilon";
155 private static final String DX_ELT = "dX";
156 private static final String DY_ELT = "dY";
157
158
159 private static final String DATA_EOP_ELT = "dataEOP";
160 private static final String TIME_SERIES_ELT = "timeSeries";
161 private static final String DATE_YEAR_ELT = "dateYear";
162 private static final String DATE_MONTH_ELT = "dateMonth";
163 private static final String DATE_DAY_ELT = "dateDay";
164 private static final String POLE_ELT = "pole";
165 private static final String UT_ELT = "UT";
166 private static final String UT1_U_UTC_ELT = "UT1_UTC";
167 private static final String NUTATION_ELT = "nutation";
168 private static final String SOURCE_ATTR = "source";
169 private static final String BULLETIN_A_SOURCE = "BulletinA";
170
171
172 private static final String FINALS_ELT = "Finals";
173 private static final String DATE_ELT = "date";
174 private static final String EOP_SET_ELT = "EOPSet";
175 private static final String BULLETIN_A_ELT = "bulletinA";
176 private static final String UT1_M_UTC_ELT = "UT1-UTC";
177
178 private boolean inBulletinA;
179 private int year;
180 private int month;
181 private int day;
182 private int mjd;
183 private AbsoluteDate mjdDate;
184 private double dtu1;
185 private double lod;
186 private double x;
187 private double y;
188 private double dpsi;
189 private double deps;
190 private double dx;
191 private double dy;
192
193
194
195
196 private final String name;
197
198
199 private final StringBuffer buffer;
200
201
202 private DataFileContent content;
203
204
205 private ITRFVersionLoader.ITRFVersionConfiguration configuration;
206
207
208
209
210 EOPContentHandler(final String name) {
211 this.name = name;
212 this.buffer = new StringBuffer();
213 }
214
215
216 @Override
217 public void startDocument() {
218 content = DataFileContent.UNKNOWN;
219 configuration = null;
220 }
221
222
223 @Override
224 public void characters(final char[] ch, final int start, final int length) {
225 buffer.append(ch, start, length);
226 }
227
228
229 @Override
230 public void startElement(final String uri, final String localName,
231 final String qName, final Attributes atts) {
232
233
234 buffer.delete(0, buffer.length());
235
236 if (content == DataFileContent.UNKNOWN) {
237
238 if (qName.equals(TIME_SERIES_ELT)) {
239
240 content = DataFileContent.DAILY;
241 } else if (qName.equals(FINALS_ELT)) {
242
243 content = DataFileContent.FINAL;
244 }
245 }
246
247 if (content == DataFileContent.DAILY) {
248 startDailyElement(qName, atts);
249 } else if (content == DataFileContent.FINAL) {
250 startFinalElement(qName);
251 }
252
253 }
254
255
256
257
258
259 private void startDailyElement(final String qName, final Attributes atts) {
260 if (qName.equals(TIME_SERIES_ELT)) {
261
262 resetEOPData();
263 } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) {
264 final String source = atts.getValue(SOURCE_ATTR);
265 if (source != null) {
266 inBulletinA = source.equals(BULLETIN_A_SOURCE);
267 }
268 }
269 }
270
271
272
273
274 private void startFinalElement(final String qName) {
275 if (qName.equals(EOP_SET_ELT)) {
276
277 resetEOPData();
278 } else if (qName.equals(BULLETIN_A_ELT)) {
279 inBulletinA = true;
280 }
281 }
282
283
284
285 private void resetEOPData() {
286 inBulletinA = false;
287 year = -1;
288 month = -1;
289 day = -1;
290 mjd = -1;
291 mjdDate = null;
292 dtu1 = Double.NaN;
293 lod = Double.NaN;
294 x = Double.NaN;
295 y = Double.NaN;
296 dpsi = Double.NaN;
297 deps = Double.NaN;
298 dx = Double.NaN;
299 dy = Double.NaN;
300 }
301
302
303 @Override
304 public void endElement(final String uri, final String localName, final String qName)
305 throws SAXException {
306 try {
307 if (content == DataFileContent.DAILY) {
308 endDailyElement(qName);
309 } else if (content == DataFileContent.FINAL) {
310 endFinalElement(qName);
311 }
312 } catch (OrekitException oe) {
313 throw new SAXException(oe);
314 }
315 }
316
317
318
319
320 private void endDailyElement(final String qName) {
321 if (qName.equals(DATE_YEAR_ELT) && (buffer.length() > 0)) {
322 year = Integer.parseInt(buffer.toString());
323 } else if (qName.equals(DATE_MONTH_ELT) && (buffer.length() > 0)) {
324 month = Integer.parseInt(buffer.toString());
325 } else if (qName.equals(DATE_DAY_ELT) && (buffer.length() > 0)) {
326 day = Integer.parseInt(buffer.toString());
327 } else if (qName.equals(MJD_ELT) && (buffer.length() > 0)) {
328 mjd = Integer.parseInt(buffer.toString());
329 mjdDate = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
330 getUtc());
331 } else if (qName.equals(UT1_M_UTC_ELT)) {
332 dtu1 = overwrite(dtu1, 1.0);
333 } else if (qName.equals(LOD_ELT)) {
334 lod = overwrite(lod, MILLI_SECONDS_TO_SECONDS);
335 } else if (qName.equals(X_ELT)) {
336 x = overwrite(x, Constants.ARC_SECONDS_TO_RADIANS);
337 } else if (qName.equals(Y_ELT)) {
338 y = overwrite(y, Constants.ARC_SECONDS_TO_RADIANS);
339 } else if (qName.equals(DPSI_ELT)) {
340 dpsi = overwrite(dpsi, MILLI_ARC_SECONDS_TO_RADIANS);
341 } else if (qName.equals(DEPSILON_ELT)) {
342 deps = overwrite(deps, MILLI_ARC_SECONDS_TO_RADIANS);
343 } else if (qName.equals(DX_ELT)) {
344 dx = overwrite(dx, MILLI_ARC_SECONDS_TO_RADIANS);
345 } else if (qName.equals(DY_ELT)) {
346 dy = overwrite(dy, MILLI_ARC_SECONDS_TO_RADIANS);
347 } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) {
348 inBulletinA = false;
349 } else if (qName.equals(DATA_EOP_ELT)) {
350 checkDates();
351 if ((!Double.isNaN(dtu1)) && (!Double.isNaN(lod)) && (!Double.isNaN(x)) && (!Double.isNaN(y))) {
352 final double[] equinox;
353 final double[] nro;
354 if (Double.isNaN(dpsi)) {
355 nro = new double[] {
356 dx, dy
357 };
358 equinox = getConverter().toEquinox(mjdDate, nro[0], nro[1]);
359 } else {
360 equinox = new double[] {
361 dpsi, deps
362 };
363 nro = getConverter().toNonRotating(mjdDate, equinox[0], equinox[1]);
364 }
365 if (configuration == null || !configuration.isValid(mjd)) {
366
367 configuration = getItrfVersionProvider().getConfiguration(name, mjd);
368 }
369 history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1],
370 configuration.getVersion(), mjdDate));
371 }
372 }
373 }
374
375
376
377
378 private void endFinalElement(final String qName) {
379 if (qName.equals(DATE_ELT) && (buffer.length() > 0)) {
380 final String[] fields = buffer.toString().split("-");
381 if (fields.length == 3) {
382 year = Integer.parseInt(fields[0]);
383 month = Integer.parseInt(fields[1]);
384 day = Integer.parseInt(fields[2]);
385 }
386 } else if (qName.equals(MJD_ELT) && (buffer.length() > 0)) {
387 mjd = Integer.parseInt(buffer.toString());
388 mjdDate = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
389 getUtc());
390 } else if (qName.equals(UT1_U_UTC_ELT)) {
391 dtu1 = overwrite(dtu1, 1.0);
392 } else if (qName.equals(LOD_ELT)) {
393 lod = overwrite(lod, MILLI_SECONDS_TO_SECONDS);
394 } else if (qName.equals(X_ELT)) {
395 x = overwrite(x, Constants.ARC_SECONDS_TO_RADIANS);
396 } else if (qName.equals(Y_ELT)) {
397 y = overwrite(y, Constants.ARC_SECONDS_TO_RADIANS);
398 } else if (qName.equals(DPSI_ELT)) {
399 dpsi = overwrite(dpsi, MILLI_ARC_SECONDS_TO_RADIANS);
400 } else if (qName.equals(DEPSILON_ELT)) {
401 deps = overwrite(deps, MILLI_ARC_SECONDS_TO_RADIANS);
402 } else if (qName.equals(DX_ELT)) {
403 dx = overwrite(dx, MILLI_ARC_SECONDS_TO_RADIANS);
404 } else if (qName.equals(DY_ELT)) {
405 dy = overwrite(dy, MILLI_ARC_SECONDS_TO_RADIANS);
406 } else if (qName.equals(BULLETIN_A_ELT)) {
407 inBulletinA = false;
408 } else if (qName.equals(EOP_SET_ELT)) {
409 checkDates();
410 if ((!Double.isNaN(dtu1)) && (!Double.isNaN(lod)) && (!Double.isNaN(x)) && (!Double.isNaN(y))) {
411 final double[] equinox;
412 final double[] nro;
413 if (Double.isNaN(dpsi)) {
414 nro = new double[] {
415 dx, dy
416 };
417 equinox = getConverter().toEquinox(mjdDate, nro[0], nro[1]);
418 } else {
419 equinox = new double[] {
420 dpsi, deps
421 };
422 nro = getConverter().toNonRotating(mjdDate, equinox[0], equinox[1]);
423 }
424 if (configuration == null || !configuration.isValid(mjd)) {
425
426 configuration = getItrfVersionProvider().getConfiguration(name, mjd);
427 }
428 history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1],
429 configuration.getVersion(), mjdDate));
430 }
431 }
432 }
433
434
435
436
437
438
439 private double overwrite(final double oldValue, final double factor) {
440 if (buffer.length() == 0) {
441
442 return oldValue;
443 } else if (inBulletinA && (!Double.isNaN(oldValue))) {
444
445 return oldValue;
446 } else {
447
448 return Double.parseDouble(buffer.toString()) * factor;
449 }
450 }
451
452
453
454 private void checkDates() {
455 if (new DateComponents(year, month, day).getMJD() != mjd) {
456 throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
457 name, year, month, day, mjd);
458 }
459 }
460
461 }
462
463 }
464
465
466 private enum DataFileContent {
467
468
469 UNKNOWN,
470
471
472 DAILY,
473
474
475 FINAL
476
477 }
478
479 }