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.Function;
22 import java.util.function.ToDoubleFunction;
23
24 import org.orekit.errors.OrekitException;
25 import org.orekit.errors.OrekitMessages;
26 import org.orekit.time.AbsoluteDate;
27 import org.orekit.time.DateComponents;
28 import org.orekit.time.TimeStamped;
29
30 /** This class holds an Earth Orientation Parameters entry.
31 * @author Luc Maisonobe
32 */
33 public class EOPEntry implements TimeStamped, Serializable {
34
35 /** Serializable UID. */
36 @Serial
37 private static final long serialVersionUID = 20260923L;
38
39 /** Entry date (modified julian day, 00h00 UTC scale). */
40 private final int mjd;
41
42 /** Entry date (absolute date). */
43 private final AbsoluteDate date;
44
45 /** UT1-UTC. */
46 private final double dt;
47
48 /** Length of day. */
49 private final double lod;
50
51 /** X component of pole motion. */
52 private final double x;
53
54 /** Y component of pole motion. */
55 private final double y;
56
57 /** X component of pole motion rate.
58 * @since 12.0
59 */
60 private final double xRate;
61
62 /** Y component of pole motion rate.
63 * @since 12.0
64 */
65 private final double yRate;
66
67 /** Correction for nutation in longitude. */
68 private final double ddPsi;
69
70 /** Correction for nutation in obliquity. */
71 private final double ddEps;
72
73 /** Correction for nutation in Celestial Intermediate Pole (CIP) coordinates. */
74 private final double dx;
75
76 /** Correction for nutation in Celestial Intermediate Pole (CIP) coordinates. */
77 private final double dy;
78
79 /** ITRF version this entry defines. */
80 private final ITRFVersion itrfType;
81
82 /** EOP data type. */
83 private final EopDataType eopDataType;
84
85 /** Origin for X, Y, UT1-UTC and LOD.
86 * @since 14.0
87 */
88 private final EOPOrigin dtOrigin;
89
90 /** Origin for nutation.
91 * @since 14.0
92 */
93 private final EOPOrigin nutOrigin;
94
95 /** Origin for Celestial Intermediate Pole.
96 * @since 14.0
97 */
98 private final EOPOrigin cipOrigin;
99
100 /** Constructor from raw elements.
101 * @param mjd entry date (modified Julian day, 00h00 UTC scale)
102 * @param dt UT1-UTC in seconds
103 * @param lod length of day
104 * @param x X component of pole motion
105 * @param y Y component of pole motion
106 * @param xRate X component of pole motion rate (NaN if absent)
107 * @param yRate Y component of pole motion rate (NaN if absent)
108 * @param ddPsi correction for nutation in longitude δΔΨ
109 * @param ddEps correction for nutation in obliquity δΔε
110 * @param dx correction for Celestial Intermediate Pole (CIP) coordinates
111 * @param dy correction for Celestial Intermediate Pole (CIP) coordinates
112 * @param itrfType ITRF version this entry defines
113 * @param date corresponding to {@code mjd}
114 * @param eopDataType EOP data type
115 * @param dtOrigin publication date for X, Y, UT1-UTC and LOD
116 * @param nutOrigin publication date for nutation (null means same as {@code dtOrigin})
117 * @param cipOrigin publication date for Celestial Intermediate Pole (null means same as {@code dtOrigin})
118 * @since 14.0
119 */
120 public EOPEntry(final int mjd, final double dt, final double lod,
121 final double x, final double y, final double xRate, final double yRate,
122 final double ddPsi, final double ddEps,
123 final double dx, final double dy,
124 final ITRFVersion itrfType, final AbsoluteDate date, final EopDataType eopDataType,
125 final EOPOrigin dtOrigin, final EOPOrigin nutOrigin, final EOPOrigin cipOrigin) {
126 this.mjd = mjd;
127 this.date = date;
128 this.dt = dt;
129 this.lod = lod;
130 this.x = x;
131 this.y = y;
132 this.xRate = xRate;
133 this.yRate = yRate;
134 this.ddPsi = ddPsi;
135 this.ddEps = ddEps;
136 this.dx = dx;
137 this.dy = dy;
138 this.itrfType = itrfType;
139 this.eopDataType = eopDataType;
140 this.dtOrigin = dtOrigin;
141 this.nutOrigin = nutOrigin == null ? dtOrigin : nutOrigin;
142 this.cipOrigin = cipOrigin == null ? dtOrigin : cipOrigin;
143 }
144
145 /** Combination constructor.
146 * <p>
147 * This constructor allows to merge data from two potentially incomplete entries
148 * corresponding to the same date.
149 * </p>
150 * <p>
151 * Incomplete entries occur in particular when parsing Bulletin A data, as
152 * xp, yp and UT1-UTC data are published on a weekly basis for rapid data and
153 * monthly for final data with a one-month delay. The pole offsets Δδψ/Δδε and
154 * x/y are published monthly with a two months delay.
155 * </p>
156 * <p>
157 * If some fields are initialized in one entry and non-initialized (i.e. NaN) in
158 * the other argument, then the initialized fields will be used for construction.
159 * If some fields are initialized in both entries, then the one published later
160 * will be used in construction. The publication date will be set to the latest
161 * of both publication dates. This implies that entries are combined.
162 * </p>
163 * @param entry1 first entry
164 * @param entry2 second entry
165 * @since 14.0
166 */
167 public EOPEntry(final EOPEntry entry1, final EOPEntry entry2) {
168
169 // safety check
170 if (entry1.mjd != entry2.mjd) {
171 throw new OrekitException(OrekitMessages.INCOMPATIBLE_EARTH_ORIENTATION_PARAMETERS,
172 new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, entry1.mjd),
173 new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, entry2.mjd));
174 }
175
176 // the dates are the same
177 this.mjd = entry1.mjd;
178 this.date = entry1.date;
179
180 // combine fields
181 this.dt = select(entry1, entry2, entry -> entry.dt, entry -> entry.dtOrigin);
182 this.lod = select(entry1, entry2, entry -> entry.lod, entry -> entry.dtOrigin);
183 this.x = select(entry1, entry2, entry -> entry.x, entry -> entry.dtOrigin);
184 this.y = select(entry1, entry2, entry -> entry.y, entry -> entry.dtOrigin);
185 this.xRate = select(entry1, entry2, entry -> entry.xRate, entry -> entry.dtOrigin);
186 this.yRate = select(entry1, entry2, entry -> entry.yRate, entry -> entry.dtOrigin);
187 this.ddPsi = select(entry1, entry2, entry -> entry.ddPsi, entry -> entry.nutOrigin);
188 this.ddEps = select(entry1, entry2, entry -> entry.ddEps, entry -> entry.nutOrigin);
189 this.dx = select(entry1, entry2, entry -> entry.dx, entry -> entry.cipOrigin);
190 this.dy = select(entry1, entry2, entry -> entry.dy, entry -> entry.cipOrigin);
191
192 if (entry1.dtOrigin.compareTo(entry2.dtOrigin) >= 0) {
193 this.itrfType = entry1.itrfType;
194 this.eopDataType = entry1.eopDataType;
195 this.dtOrigin = entry1.dtOrigin;
196 } else {
197 this.itrfType = entry2.itrfType;
198 this.eopDataType = entry2.eopDataType;
199 this.dtOrigin = entry2.dtOrigin;
200 }
201
202 this.nutOrigin = entry1.nutOrigin.compareTo(entry2.nutOrigin) >= 0 ? entry1.nutOrigin : entry2.nutOrigin;
203 this.cipOrigin = entry1.cipOrigin.compareTo(entry2.cipOrigin) >= 0 ? entry1.cipOrigin : entry2.cipOrigin;
204
205 }
206
207 /** Select either initialized or published last EOP field.
208 * @param entry1 first entry
209 * @param entry2 second entry
210 * @param field selector for field
211 * @param origin selector for origin
212 * @return selected field
213 * @since 14.0
214 */
215 private double select(final EOPEntry entry1, final EOPEntry entry2,
216 final ToDoubleFunction<EOPEntry> field,
217 final Function<EOPEntry, EOPOrigin> origin) {
218 final double field1 = field.applyAsDouble(entry1);
219 final double field2 = field.applyAsDouble(entry2);
220 if (Double.isNaN(field1)) {
221 // the field is not initialized in entry1, we select the field from entry2
222 return field2;
223 } else if (Double.isNaN(field2)) {
224 // the field is not initialized in entry2, we select the field from entry1
225 return field1;
226 } else {
227 // the field is initialized in both entries, we select the one published later
228 return origin.apply(entry1).compareTo(origin.apply(entry2)) >= 0 ? field1 : field2;
229 }
230 }
231
232 /** Get the entry date (modified julian day, 00h00 UTC scale).
233 * @return entry date
234 * @see #getDate()
235 */
236 public int getMjd() {
237 return mjd;
238 }
239
240 /** {@inheritDoc} */
241 public AbsoluteDate getDate() {
242 return date;
243 }
244
245 /** Get the UT1-UTC value.
246 * @return UT1-UTC in seconds
247 */
248 public double getUT1MinusUTC() {
249 return dt;
250 }
251
252 /** Get the LoD (Length of Day) value.
253 * @return LoD in seconds
254 */
255 public double getLOD() {
256 return lod;
257 }
258
259 /** Get the X component of the pole motion.
260 * @return X component of pole motion
261 */
262 public double getX() {
263 return x;
264 }
265
266 /** Get the Y component of the pole motion.
267 * @return Y component of pole motion
268 */
269 public double getY() {
270 return y;
271 }
272
273 /** Get the X component of the pole motion rate.
274 * @return X component of pole motion rate
275 * @since 12.0
276 */
277 public double getXRate() {
278 return xRate;
279 }
280
281 /** Get the Y component of the pole motion rate.
282 * @return Y component of pole motion rate
283 * @since 12.0
284 */
285 public double getYRate() {
286 return yRate;
287 }
288
289 /** Get the correction for nutation in longitude δΔΨ.
290 * @return correction for nutation in longitude δΔΨ
291 */
292 public double getDdPsi() {
293 return ddPsi;
294 }
295
296 /** Get the correction for nutation in obliquity δΔε.
297 * @return correction for nutation in obliquity δΔε
298 */
299 public double getDdEps() {
300 return ddEps;
301 }
302
303 /** Get the correction for Celestial Intermediate Pole (CIP) coordinates.
304 * @return correction for Celestial Intermediate Pole (CIP) coordinates
305 */
306 public double getDx() {
307 return dx;
308 }
309
310 /** Get the correction for Celestial Intermediate Pole (CIP) coordinates.
311 * @return correction for Celestial Intermediate Pole (CIP) coordinates
312 */
313 public double getDy() {
314 return dy;
315 }
316
317 /** Get the ITRF version this entry defines.
318 * @return ITRF version this entry defines
319 * @since 9.2
320 */
321 public ITRFVersion getITRFType() {
322 return itrfType;
323 }
324
325 /** Get the EOP data type.
326 * @return EOP data type
327 * @since 13.1.1
328 */
329 public EopDataType getEopDataType() { return eopDataType; }
330
331 /** Get the origin for X, Y, UT1-UTC and LOD.
332 * @return origin for X, Y, UT1-UTC and LOD
333 * @since 14.0
334 */
335 public EOPOrigin getDtOrigin() {
336 return dtOrigin;
337 }
338
339 /** Get the origin for nutation.
340 * @return origin for nutation
341 * @since 14.0
342 */
343 public EOPOrigin getNutOrigin() {
344 return nutOrigin;
345 }
346
347 /** Get the origin for Celestial Intermediate Pole.
348 * @return origin for Celestial Intermediate Pole
349 * @since 14.0
350 */
351 public EOPOrigin getCipOrigin() {
352 return cipOrigin;
353 }
354
355 }