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 }