1 /* Copyright 2002-2026 CS GROUP
2 * Licensed to CS GROUP (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.orekit.frames;
18
19 import java.io.Serial;
20 import java.io.Serializable;
21 import java.util.function.ToDoubleFunction;
22 import java.util.function.ToIntFunction;
23
24 import org.hipparchus.util.FastMath;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitMessages;
27 import org.orekit.time.AbsoluteDate;
28 import org.orekit.time.DateComponents;
29 import org.orekit.time.TimeStamped;
30
31 /** This class holds an Earth Orientation Parameters entry.
32 * @author Luc Maisonobe
33 */
34 public class EOPEntry implements TimeStamped, Serializable {
35
36 /** Serializable UID. */
37 @Serial
38 private static final long serialVersionUID = 20260729L;
39
40 /** Entry date (modified julian day, 00h00 UTC scale). */
41 private final int mjd;
42
43 /** Entry date (absolute date). */
44 private final AbsoluteDate date;
45
46 /** UT1-UTC. */
47 private final double dt;
48
49 /** Length of day. */
50 private final double lod;
51
52 /** X component of pole motion. */
53 private final double x;
54
55 /** Y component of pole motion. */
56 private final double y;
57
58 /** X component of pole motion rate.
59 * @since 12.0
60 */
61 private final double xRate;
62
63 /** Y component of pole motion rate.
64 * @since 12.0
65 */
66 private final double yRate;
67
68 /** Correction for nutation in longitude. */
69 private final double ddPsi;
70
71 /** Correction for nutation in obliquity. */
72 private final double ddEps;
73
74 /** Correction for nutation in Celestial Intermediate Pole (CIP) coordinates. */
75 private final double dx;
76
77 /** Correction for nutation in Celestial Intermediate Pole (CIP) coordinates. */
78 private final double dy;
79
80 /** ITRF version this entry defines. */
81 private final ITRFVersion itrfType;
82
83 /** EOP data type. */
84 private final EopDataType eopDataType;
85
86 /** Publication date for X, Y, UT1-UTC and LOD.
87 * @since 14.0
88 */
89 private final int dtPub;
90
91 /** Publication date for nutation.
92 * @since 14.0
93 */
94 private final int nutPub;
95
96 /** Publication date for Celestial Intermediate Pole.
97 * @since 14.0
98 */
99 private final int cipPub;
100
101 /** Constructor from raw elements.
102 * <p>
103 * This constructor assumes publication date is the same as entry date
104 * </p>
105 * @param mjd entry date (modified Julian day, 00h00 UTC scale)
106 * @param dt UT1-UTC in seconds
107 * @param lod length of day
108 * @param x X component of pole motion
109 * @param y Y component of pole motion
110 * @param xRate X component of pole motion rate (NaN if absent)
111 * @param yRate Y component of pole motion rate (NaN if absent)
112 * @param ddPsi correction for nutation in longitude δΔΨ
113 * @param ddEps correction for nutation in obliquity δΔε
114 * @param dx correction for Celestial Intermediate Pole (CIP) coordinates
115 * @param dy correction for Celestial Intermediate Pole (CIP) coordinates
116 * @param itrfType ITRF version this entry defines
117 * @param date corresponding to {@code mjd}
118 * @param eopDataType EOP data type
119 * @since 13.1.1
120 */
121 public EOPEntry(final int mjd, final double dt, final double lod,
122 final double x, final double y, final double xRate, final double yRate,
123 final double ddPsi, final double ddEps,
124 final double dx, final double dy,
125 final ITRFVersion itrfType, final AbsoluteDate date,
126 final EopDataType eopDataType) {
127 this(mjd, dt, lod, x, y, xRate, yRate, ddPsi, ddEps, dx, dy, itrfType, date, eopDataType,
128 mjd, mjd, mjd);
129 }
130
131 /** Constructor from raw elements.
132 * @param mjd entry date (modified Julian day, 00h00 UTC scale)
133 * @param dt UT1-UTC in seconds
134 * @param lod length of day
135 * @param x X component of pole motion
136 * @param y Y component of pole motion
137 * @param xRate X component of pole motion rate (NaN if absent)
138 * @param yRate Y component of pole motion rate (NaN if absent)
139 * @param ddPsi correction for nutation in longitude δΔΨ
140 * @param ddEps correction for nutation in obliquity δΔε
141 * @param dx correction for Celestial Intermediate Pole (CIP) coordinates
142 * @param dy correction for Celestial Intermediate Pole (CIP) coordinates
143 * @param itrfType ITRF version this entry defines
144 * @param date corresponding to {@code mjd}
145 * @param eopDataType EOP data type
146 * @param dtPub publication date for X, Y, UT1-UTC and LOD
147 * @param nutPub publication date for nutation
148 * @param cipPub publication date for Celestial Intermediate Pole
149 * @since 14.0
150 */
151 public EOPEntry(final int mjd, final double dt, final double lod,
152 final double x, final double y, final double xRate, final double yRate,
153 final double ddPsi, final double ddEps,
154 final double dx, final double dy,
155 final ITRFVersion itrfType, final AbsoluteDate date, final EopDataType eopDataType,
156 final int dtPub, final int nutPub, final int cipPub) {
157 this.mjd = mjd;
158 this.date = date;
159 this.dt = dt;
160 this.lod = lod;
161 this.x = x;
162 this.y = y;
163 this.xRate = xRate;
164 this.yRate = yRate;
165 this.ddPsi = ddPsi;
166 this.ddEps = ddEps;
167 this.dx = dx;
168 this.dy = dy;
169 this.itrfType = itrfType;
170 this.eopDataType = eopDataType;
171 this.dtPub = dtPub;
172 this.nutPub = nutPub;
173 this.cipPub = cipPub;
174 }
175
176 /** Combination constructor.
177 * <p>
178 * This constructor allows to merge data from two potentially incomplete entries
179 * corresponding to the same date.
180 * </p>
181 * <p>
182 * Incomplete entries occur in particular when parsing Bulletin A data, as
183 * xp, yp and UT1-UTC data are published on a weekly basis for rapid data and
184 * monthly for final data with a one-month delay. The pole offsets Δδψ/Δδε and
185 * x/y are published monthly with a two months delay.
186 * </p>
187 * <p>
188 * If some fields are initialized in one entry and non-initialized (i.e. NaN) in
189 * the other argument, then the initialized fields will be used for construction.
190 * If some fields are initialized in both entries, then the one published later
191 * will be used in construction. The publication date will be set to the latest
192 * of both publication dates. This implies that entries are combined.
193 * </p>
194 * @param entry1 first entry
195 * @param entry2 second entry
196 * @since 14.0
197 */
198 public EOPEntry(final EOPEntry entry1, final EOPEntry entry2) {
199
200 // safety check
201 if (entry1.mjd != entry2.mjd) {
202 throw new OrekitException(OrekitMessages.INCOMPATIBLE_EARTH_ORIENTATION_PARAMETERS,
203 new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, entry1.mjd),
204 new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, entry2.mjd));
205 }
206
207 // the dates are the same
208 this.mjd = entry1.mjd;
209 this.date = entry1.date;
210
211 // combine fields
212 this.dt = select(entry1, entry2, entry -> entry.dt, entry -> entry.dtPub);
213 this.lod = select(entry1, entry2, entry -> entry.lod, entry -> entry.dtPub);
214 this.x = select(entry1, entry2, entry -> entry.x, entry -> entry.dtPub);
215 this.y = select(entry1, entry2, entry -> entry.y, entry -> entry.dtPub);
216 this.xRate = select(entry1, entry2, entry -> entry.xRate, entry -> entry.dtPub);
217 this.yRate = select(entry1, entry2, entry -> entry.yRate, entry -> entry.dtPub);
218 this.ddPsi = select(entry1, entry2, entry -> entry.ddPsi, entry -> entry.nutPub);
219 this.ddEps = select(entry1, entry2, entry -> entry.ddEps, entry -> entry.nutPub);
220 this.dx = select(entry1, entry2, entry -> entry.dx, entry -> entry.cipPub);
221 this.dy = select(entry1, entry2, entry -> entry.dy, entry -> entry.cipPub);
222
223 if (entry1.dtPub >= entry2.dtPub) {
224 this.itrfType = entry1.itrfType;
225 this.eopDataType = entry1.eopDataType;
226 this.dtPub = entry1.dtPub;
227 } else {
228 this.itrfType = entry2.itrfType;
229 this.eopDataType = entry2.eopDataType;
230 this.dtPub = entry2.dtPub;
231 }
232
233 this.nutPub = FastMath.max(entry1.nutPub, entry2.nutPub);
234 this.cipPub = FastMath.max(entry1.cipPub, entry2.cipPub);
235
236 }
237
238 /** Select either initialized or published last EOP field.
239 * @param entry1 first entry
240 * @param entry2 second entry
241 * @param field selector for field
242 * @param publication selector for publication date
243 * @return selected field
244 * @since 14.0
245 */
246 private double select(final EOPEntry entry1, final EOPEntry entry2,
247 final ToDoubleFunction<EOPEntry> field,
248 final ToIntFunction<EOPEntry> publication) {
249 final double field1 = field.applyAsDouble(entry1);
250 final double field2 = field.applyAsDouble(entry2);
251 if (Double.isNaN(field1)) {
252 // the field is not initialized in entry1, we select the field from entry2
253 return field2;
254 } else if (Double.isNaN(field2)) {
255 // the field is not initialized in entry2, we select the field from entry1
256 return field1;
257 } else {
258 // the field is initialized in both entries, we select the one published later
259 return publication.applyAsInt(entry1) >= publication.applyAsInt(entry2) ?
260 field1 : field2;
261 }
262 }
263
264 /** Get the entry date (modified julian day, 00h00 UTC scale).
265 * @return entry date
266 * @see #getDate()
267 */
268 public int getMjd() {
269 return mjd;
270 }
271
272 /** {@inheritDoc} */
273 public AbsoluteDate getDate() {
274 return date;
275 }
276
277 /** Get the UT1-UTC value.
278 * @return UT1-UTC in seconds
279 */
280 public double getUT1MinusUTC() {
281 return dt;
282 }
283
284 /** Get the LoD (Length of Day) value.
285 * @return LoD in seconds
286 */
287 public double getLOD() {
288 return lod;
289 }
290
291 /** Get the X component of the pole motion.
292 * @return X component of pole motion
293 */
294 public double getX() {
295 return x;
296 }
297
298 /** Get the Y component of the pole motion.
299 * @return Y component of pole motion
300 */
301 public double getY() {
302 return y;
303 }
304
305 /** Get the X component of the pole motion rate.
306 * @return X component of pole motion rate
307 * @since 12.0
308 */
309 public double getXRate() {
310 return xRate;
311 }
312
313 /** Get the Y component of the pole motion rate.
314 * @return Y component of pole motion rate
315 * @since 12.0
316 */
317 public double getYRate() {
318 return yRate;
319 }
320
321 /** Get the correction for nutation in longitude δΔΨ.
322 * @return correction for nutation in longitude δΔΨ
323 */
324 public double getDdPsi() {
325 return ddPsi;
326 }
327
328 /** Get the correction for nutation in obliquity δΔε.
329 * @return correction for nutation in obliquity δΔε
330 */
331 public double getDdEps() {
332 return ddEps;
333 }
334
335 /** Get the correction for Celestial Intermediate Pole (CIP) coordinates.
336 * @return correction for Celestial Intermediate Pole (CIP) coordinates
337 */
338 public double getDx() {
339 return dx;
340 }
341
342 /** Get the correction for Celestial Intermediate Pole (CIP) coordinates.
343 * @return correction for Celestial Intermediate Pole (CIP) coordinates
344 */
345 public double getDy() {
346 return dy;
347 }
348
349 /** Get the ITRF version this entry defines.
350 * @return ITRF version this entry defines
351 * @since 9.2
352 */
353 public ITRFVersion getITRFType() {
354 return itrfType;
355 }
356
357 /** Get the EOP data type.
358 * @return EOP data type
359 * @since 13.1.1
360 */
361 public EopDataType getEopDataType() { return eopDataType; }
362
363 /** Get the publication date for X, Y, UT1-UTC and LOD (modified Julian day).
364 * @return publication date for X, Y, UT1-UTC and LOD
365 * @since 14.0
366 */
367 public int getDtPub() {
368 return dtPub;
369 }
370
371 /** Get the publication date for nutation (modified Julian day).
372 * @return publication date for nutation
373 * @since 14.0
374 */
375 public int getNutPub() {
376 return nutPub;
377 }
378
379 /** Get the publication date for Celestial Intermediate Pole (modified Julian day).
380 * @return publication date for Celestial Intermediate Pole
381 * @since 14.0
382 */
383 public int getCipPub() {
384 return cipPub;
385 }
386
387 }