1   /* Copyright 2002-2024 Luc Maisonobe
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.Serializable;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.List;
23  
24  import org.hipparchus.util.FastMath;
25  import org.orekit.annotation.DefaultDataContext;
26  import org.orekit.errors.OrekitException;
27  import org.orekit.errors.OrekitInternalError;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.utils.Constants;
30  import org.orekit.utils.IERSConventions.NutationCorrectionConverter;
31  import org.orekit.utils.SecularAndHarmonic;
32  
33  /** This class extends an {@link EOPHistory} for some weeks using fitting.
34   * <p>
35   * The goal of this class is to provide a reasonable prediction of
36   * Earth Orientation Parameters past the last date available in
37   * regular {@link EOPHistory}, which just generated corrections set
38   * to 0 when they have no data.
39   * </p>
40   * <p>
41   * The prediction is based on fitting of last data, with both
42   * {@link SecularAndHarmonic secular (polynomial) and harmonic (periodic)}
43   * terms. The extended entries are generated at one point per day
44   * and are continuous (i.e. no leap seconds are introduced)
45   * </p>
46   * <p>
47   * After construction, the history contains both the initial
48   * raw history and an extension part appended after it.
49   * </p>
50   * @see EOPFitter
51   * @see SecularAndHarmonic
52   * @since 12.0
53   * @author Luc Maisonobe
54   */
55  public class PredictedEOPHistory extends EOPHistory implements Serializable {
56  
57      /** Serializable UID. */
58      private static final long serialVersionUID = 20230309L;
59  
60      /** Raw EOP history to extend. */
61      private final EOPHistory rawHistory;
62  
63      /** Duration of the extension period (s). */
64      private final double extensionDuration;
65  
66      /** Fitter for all Earth Orientation Parameters. */
67      private final EOPFitter fitter;
68  
69      /** Simple constructor.
70       * @param rawHistory raw EOP history to extend.
71       * @param extensionDuration duration of the extension period (s)
72       * @param fitter fitter for all Earth Orientation Parameters
73       */
74      public PredictedEOPHistory(final EOPHistory rawHistory, final double extensionDuration,
75                                 final EOPFitter fitter) {
76          super(rawHistory.getConventions(), rawHistory.getInterpolationDegree(),
77                extendHistory(rawHistory, extensionDuration, fitter),
78                rawHistory.isSimpleEop(), rawHistory.getTimeScales());
79          this.rawHistory        = rawHistory;
80          this.extensionDuration = extensionDuration;
81          this.fitter            = fitter;
82      }
83  
84      /** Extends raw history.
85       * @param rawHistory raw EOP history to extend.
86       * @param extensionDuration duration of the extension period (s)
87       * @param fitter fitter for all Earth Orientation Parameters
88       * @return extended history
89       */
90      private static Collection<? extends EOPEntry> extendHistory(final EOPHistory rawHistory,
91                                                                  final double extensionDuration,
92                                                                  final EOPFitter fitter) {
93  
94  
95          // fit model
96          final EOPFittedModel model = fitter.fit(rawHistory);
97  
98          // create a converter for nutation corrections
99          final NutationCorrectionConverter converter =
100                         rawHistory.getConventions().getNutationCorrectionConverter(rawHistory.getTimeScales());
101 
102         // generate extension entries
103         final List<EOPEntry> rawEntries = rawHistory.getEntries();
104         final EOPEntry       last       = rawEntries.get(rawEntries.size() - 1);
105         final int n = (int) FastMath.rint(extensionDuration / Constants.JULIAN_DAY);
106         final List<EOPEntry> entries = new ArrayList<>(rawEntries.size() + n);
107         entries.addAll(rawEntries);
108         for (int i = 0; i < n; ++i) {
109             final AbsoluteDate date = last.getDate().shiftedBy((i + 1) * Constants.JULIAN_DAY);
110             final double dut1   = model.getDUT1().osculatingValue(date);
111             final double lod    = -Constants.JULIAN_DAY * model.getDUT1().osculatingDerivative(date);
112             final double xp     = model.getXp().osculatingValue(date);
113             final double yp     = model.getYp().osculatingValue(date);
114             final double xpRate = model.getXp().osculatingDerivative(date);
115             final double ypRate = model.getYp().osculatingDerivative(date);
116             final double dx     = model.getDx().osculatingValue(date);
117             final double dy     = model.getDy().osculatingValue(date);
118             final double[] equinox = converter.toEquinox(date, dx, dy);
119             entries.add(new EOPEntry(last.getMjd() + i + 1, dut1, lod, xp, yp, xpRate, ypRate,
120                                      equinox[0], equinox[1], dx, dy,
121                                      last.getITRFType(), date));
122         }
123 
124         return entries;
125 
126     }
127 
128     /** Replace the instance with a data transfer object for serialization.
129      * @return data transfer object that will be serialized
130      */
131     @DefaultDataContext
132     private Object writeReplace() {
133         return new DataTransferObject(rawHistory, extensionDuration, fitter);
134     }
135 
136     /** Internal class used only for serialization. */
137     @DefaultDataContext
138     private static class DataTransferObject implements Serializable {
139 
140         /** Serializable UID. */
141         private static final long serialVersionUID = 20230309L;
142 
143         /** Raw EOP history to extend. */
144         private final EOPHistory rawHistory;
145 
146         /** Duration of the extension period (s). */
147         private final double extensionDuration;
148 
149         /** Fitter for all Earth Orientation Parameters. */
150         private final EOPFitter fitter;
151 
152         /** Simple constructor.
153          * @param rawHistory raw EOP history to extend.
154          * @param extensionDuration duration of the extension period (s)
155          * @param fitter fitter for all Earth Orientation Parameters
156          */
157         DataTransferObject(final EOPHistory rawHistory, final double extensionDuration, final EOPFitter fitter) {
158             this.rawHistory        = rawHistory;
159             this.extensionDuration = extensionDuration;
160             this.fitter            = fitter;
161         }
162 
163         /** Replace the deserialized data transfer object with a {@link PredictedEOPHistory}.
164          * @return replacement {@link PredictedEOPHistory}
165          */
166         private Object readResolve() {
167             try {
168                 return new PredictedEOPHistory(rawHistory, extensionDuration, fitter);
169             } catch (OrekitException oe) {
170                 throw new OrekitInternalError(oe);
171             }
172         }
173 
174     }
175 
176 }