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
60
61
62
63 class RapidDataAndPredictionXMLLoader extends AbstractEopLoader
64 implements EOPHistoryLoader {
65
66
67 private static final Unit MILLI_SECOND = Unit.parse("ms");
68
69
70 private static final Unit MILLI_ARC_SECOND = Unit.parse("mas");
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 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 DPSI_ELT = "dPsi";
147 private static final String DEPSILON_ELT = "dEpsilon";
148 private static final String DX_ELT = "dX";
149 private static final String DY_ELT = "dY";
150
151
152 private static final String DATA_EOP_ELT = "dataEOP";
153 private static final String TIME_SERIES_ELT = "timeSeries";
154 private static final String DATE_YEAR_ELT = "dateYear";
155 private static final String DATE_MONTH_ELT = "dateMonth";
156 private static final String DATE_DAY_ELT = "dateDay";
157 private static final String POLE_ELT = "pole";
158 private static final String UT_ELT = "UT";
159 private static final String UT1_U_UTC_ELT = "UT1_UTC";
160 private static final String NUTATION_ELT = "nutation";
161 private static final String SOURCE_ATTR = "source";
162 private static final String BULLETIN_A_SOURCE = "BulletinA";
163
164
165 private static final String FINALS_ELT = "Finals";
166 private static final String DATE_ELT = "date";
167 private static final String EOP_SET_ELT = "EOPSet";
168 private static final String BULLETIN_A_ELT = "bulletinA";
169 private static final String UT1_M_UTC_ELT = "UT1-UTC";
170
171 private boolean inBulletinA;
172 private int year;
173 private int month;
174 private int day;
175 private int mjd;
176 private AbsoluteDate mjdDate;
177 private double dtu1;
178 private double lod;
179 private double x;
180 private double y;
181 private double dpsi;
182 private double deps;
183 private double dx;
184 private double dy;
185
186
187
188
189 private final String name;
190
191
192 private final StringBuffer buffer;
193
194
195 private DataFileContent content;
196
197
198 private ITRFVersionLoader.ITRFVersionConfiguration configuration;
199
200
201
202
203 EOPContentHandler(final String name) {
204 this.name = name;
205 this.buffer = new StringBuffer();
206 }
207
208
209 @Override
210 public void startDocument() {
211 content = DataFileContent.UNKNOWN;
212 configuration = null;
213 }
214
215
216 @Override
217 public void characters(final char[] ch, final int start, final int length) {
218 buffer.append(ch, start, length);
219 }
220
221
222 @Override
223 public void startElement(final String uri, final String localName,
224 final String qName, final Attributes atts) {
225
226
227 buffer.delete(0, buffer.length());
228
229 if (content == DataFileContent.UNKNOWN) {
230
231 if (qName.equals(TIME_SERIES_ELT)) {
232
233 content = DataFileContent.DAILY;
234 } else if (qName.equals(FINALS_ELT)) {
235
236 content = DataFileContent.FINAL;
237 }
238 }
239
240 if (content == DataFileContent.DAILY) {
241 startDailyElement(qName, atts);
242 } else if (content == DataFileContent.FINAL) {
243 startFinalElement(qName);
244 }
245
246 }
247
248
249
250
251
252 private void startDailyElement(final String qName, final Attributes atts) {
253 if (qName.equals(TIME_SERIES_ELT)) {
254
255 resetEOPData();
256 } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) {
257 final String source = atts.getValue(SOURCE_ATTR);
258 if (source != null) {
259 inBulletinA = source.equals(BULLETIN_A_SOURCE);
260 }
261 }
262 }
263
264
265
266
267 private void startFinalElement(final String qName) {
268 if (qName.equals(EOP_SET_ELT)) {
269
270 resetEOPData();
271 } else if (qName.equals(BULLETIN_A_ELT)) {
272 inBulletinA = true;
273 }
274 }
275
276
277
278 private void resetEOPData() {
279 inBulletinA = false;
280 year = -1;
281 month = -1;
282 day = -1;
283 mjd = -1;
284 mjdDate = null;
285 dtu1 = Double.NaN;
286 lod = Double.NaN;
287 x = Double.NaN;
288 y = Double.NaN;
289 dpsi = Double.NaN;
290 deps = Double.NaN;
291 dx = Double.NaN;
292 dy = Double.NaN;
293 }
294
295
296 @Override
297 public void endElement(final String uri, final String localName, final String qName) {
298 if (content == DataFileContent.DAILY) {
299 endDailyElement(qName);
300 } else if (content == DataFileContent.FINAL) {
301 endFinalElement(qName);
302 }
303 }
304
305
306
307
308 private void endDailyElement(final String qName) {
309 if (qName.equals(DATE_YEAR_ELT) && buffer.length() > 0) {
310 year = Integer.parseInt(buffer.toString());
311 } else if (qName.equals(DATE_MONTH_ELT) && buffer.length() > 0) {
312 month = Integer.parseInt(buffer.toString());
313 } else if (qName.equals(DATE_DAY_ELT) && buffer.length() > 0) {
314 day = Integer.parseInt(buffer.toString());
315 } else if (qName.equals(MJD_ELT) && buffer.length() > 0) {
316 mjd = Integer.parseInt(buffer.toString());
317 mjdDate = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
318 getUtc());
319 } else if (qName.equals(UT1_M_UTC_ELT)) {
320 dtu1 = overwrite(dtu1, Unit.SECOND);
321 } else if (qName.equals(LOD_ELT)) {
322 lod = overwrite(lod, MILLI_SECOND);
323 } else if (qName.equals(X_ELT)) {
324 x = overwrite(x, Unit.ARC_SECOND);
325 } else if (qName.equals(Y_ELT)) {
326 y = overwrite(y, Unit.ARC_SECOND);
327 } else if (qName.equals(DPSI_ELT)) {
328 dpsi = overwrite(dpsi, MILLI_ARC_SECOND);
329 } else if (qName.equals(DEPSILON_ELT)) {
330 deps = overwrite(deps, MILLI_ARC_SECOND);
331 } else if (qName.equals(DX_ELT)) {
332 dx = overwrite(dx, MILLI_ARC_SECOND);
333 } else if (qName.equals(DY_ELT)) {
334 dy = overwrite(dy, MILLI_ARC_SECOND);
335 } else if (qName.equals(POLE_ELT) || qName.equals(UT_ELT) || qName.equals(NUTATION_ELT)) {
336 inBulletinA = false;
337 } else if (qName.equals(DATA_EOP_ELT)) {
338 checkDates();
339 if (!Double.isNaN(dtu1) && !Double.isNaN(lod) && !Double.isNaN(x) && !Double.isNaN(y)) {
340 final double[] equinox;
341 final double[] nro;
342 if (Double.isNaN(dpsi)) {
343 nro = new double[] {
344 dx, dy
345 };
346 equinox = getConverter().toEquinox(mjdDate, nro[0], nro[1]);
347 } else {
348 equinox = new double[] {
349 dpsi, deps
350 };
351 nro = getConverter().toNonRotating(mjdDate, equinox[0], equinox[1]);
352 }
353 if (configuration == null || !configuration.isValid(mjd)) {
354
355 configuration = getItrfVersionProvider().getConfiguration(name, mjd);
356 }
357 history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1],
358 configuration.getVersion(), mjdDate));
359 }
360 }
361 }
362
363
364
365
366 private void endFinalElement(final String qName) {
367 if (qName.equals(DATE_ELT) && buffer.length() > 0) {
368 final String[] fields = buffer.toString().split("-");
369 if (fields.length == 3) {
370 year = Integer.parseInt(fields[0]);
371 month = Integer.parseInt(fields[1]);
372 day = Integer.parseInt(fields[2]);
373 }
374 } else if (qName.equals(MJD_ELT) && buffer.length() > 0) {
375 mjd = Integer.parseInt(buffer.toString());
376 mjdDate = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
377 getUtc());
378 } else if (qName.equals(UT1_U_UTC_ELT)) {
379 dtu1 = overwrite(dtu1, Unit.SECOND);
380 } else if (qName.equals(LOD_ELT)) {
381 lod = overwrite(lod, MILLI_SECOND);
382 } else if (qName.equals(X_ELT)) {
383 x = overwrite(x, Unit.ARC_SECOND);
384 } else if (qName.equals(Y_ELT)) {
385 y = overwrite(y, Unit.ARC_SECOND);
386 } else if (qName.equals(DPSI_ELT)) {
387 dpsi = overwrite(dpsi, MILLI_ARC_SECOND);
388 } else if (qName.equals(DEPSILON_ELT)) {
389 deps = overwrite(deps, MILLI_ARC_SECOND);
390 } else if (qName.equals(DX_ELT)) {
391 dx = overwrite(dx, MILLI_ARC_SECOND);
392 } else if (qName.equals(DY_ELT)) {
393 dy = overwrite(dy, MILLI_ARC_SECOND);
394 } else if (qName.equals(BULLETIN_A_ELT)) {
395 inBulletinA = false;
396 } else if (qName.equals(EOP_SET_ELT)) {
397 checkDates();
398 if (!Double.isNaN(dtu1) && !Double.isNaN(lod) && !Double.isNaN(x) && !Double.isNaN(y)) {
399 final double[] equinox;
400 final double[] nro;
401 if (Double.isNaN(dpsi)) {
402 nro = new double[] {
403 dx, dy
404 };
405 equinox = getConverter().toEquinox(mjdDate, nro[0], nro[1]);
406 } else {
407 equinox = new double[] {
408 dpsi, deps
409 };
410 nro = getConverter().toNonRotating(mjdDate, equinox[0], equinox[1]);
411 }
412 if (configuration == null || !configuration.isValid(mjd)) {
413
414 configuration = getItrfVersionProvider().getConfiguration(name, mjd);
415 }
416 history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1],
417 configuration.getVersion(), mjdDate));
418 }
419 }
420 }
421
422
423
424
425
426
427 private double overwrite(final double oldValue, final Unit units) {
428 if (buffer.length() == 0) {
429
430 return oldValue;
431 } else if (inBulletinA && !Double.isNaN(oldValue)) {
432
433 return oldValue;
434 } else {
435
436 return units.toSI(Double.parseDouble(buffer.toString()));
437 }
438 }
439
440
441
442 private void checkDates() {
443 if (new DateComponents(year, month, day).getMJD() != mjd) {
444 throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
445 name, year, month, day, mjd);
446 }
447 }
448
449
450 @Override
451 public InputSource resolveEntity(final String publicId, final String systemId) {
452
453 return new InputSource();
454 }
455
456 }
457
458 }
459
460
461 private enum DataFileContent {
462
463
464 UNKNOWN,
465
466
467 DAILY,
468
469
470 FINAL
471
472 }
473
474 }