1   /* Copyright 2002-2024 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.Serializable;
20  import java.util.concurrent.atomic.AtomicReference;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.orekit.annotation.DefaultDataContext;
24  import org.orekit.data.DataContext;
25  import org.orekit.time.AbsoluteDate;
26  import org.orekit.time.FieldAbsoluteDate;
27  import org.orekit.time.TimeScale;
28  
29  
30  /** Provider for a specific version of International Terrestrial Reference Frame.
31   * <p>
32   * This class ensure the ITRF it defines is a specific version, regardless of
33   * the version of the underlying {@link EOPEntry Earth Orientation Parameters}.
34   * </p>
35   * @author Luc Maisonobe
36   * @since 9.2
37   */
38  class VersionedITRFProvider implements EOPBasedTransformProvider {
39  
40      /** Serializable UID. */
41      private static final long serialVersionUID = 20180403L;
42  
43      /** ITRF version this provider should generate. */
44      private final ITRFVersion version;
45  
46      /** Raw ITRF provider. */
47      private final ITRFProvider rawProvider;
48  
49      /** Converter between different ITRF versions. */
50      private final AtomicReference<ITRFVersion.Converter> converter;
51  
52      /** TT time scale. */
53      private final TimeScale tt;
54  
55      /** Simple constructor.
56       * @param version ITRF version this provider should generate
57       * @param rawProvider raw ITRF provider
58       * @param tt TT time scale.
59       */
60      VersionedITRFProvider(final ITRFVersion version,
61                            final ITRFProvider rawProvider,
62                            final TimeScale tt) {
63          this.version     = version;
64          this.rawProvider = rawProvider;
65          this.converter   = new AtomicReference<>();
66          this.tt = tt;
67      }
68  
69      /** Get the ITRF version.
70       * @return ITRF version
71       */
72      public ITRFVersion getITRFVersion() {
73          return version;
74      }
75  
76      /** {@inheritDoc} */
77      @Override
78      public EOPHistory getEOPHistory() {
79          return rawProvider.getEOPHistory();
80      }
81  
82      /** {@inheritDoc} */
83      @Override
84      public VersionedITRFProvider getNonInterpolatingProvider() {
85          return new VersionedITRFProvider(version, rawProvider.getNonInterpolatingProvider(), tt);
86      }
87  
88      /** {@inheritDoc} */
89      @Override
90      public Transform getTransform(final AbsoluteDate date) {
91  
92          // get the transform from the current EOP
93          final Transform rawTransform = rawProvider.getTransform(date);
94  
95          // add the conversion layer
96          final ITRFVersion.Converter converterForDate = getConverter(date);
97          if (converterForDate == null) {
98              return rawTransform;
99          } else {
100             return new Transform(date, rawTransform, converterForDate.getTransform(date));
101         }
102 
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public KinematicTransform getKinematicTransform(final AbsoluteDate date) {
108 
109         // get the transform from the current EOP
110         final KinematicTransform rawTransform = rawProvider.getKinematicTransform(date);
111 
112         // add the conversion layer
113         final ITRFVersion.Converter converterForDate = getConverter(date);
114         if (converterForDate == null) {
115             return rawTransform;
116         } else {
117             return KinematicTransform.compose(date, rawTransform, converterForDate.getKinematicTransform(date));
118         }
119 
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     public StaticTransform getStaticTransform(final AbsoluteDate date) {
125 
126         // get the transform from the current EOP
127         final StaticTransform rawTransform = rawProvider.getStaticTransform(date);
128 
129         // add the conversion layer
130         final ITRFVersion.Converter converterForDate = getConverter(date);
131         if (converterForDate == null) {
132             return rawTransform;
133         } else {
134             return StaticTransform.compose(
135                     date,
136                     rawTransform,
137                     converterForDate.getStaticTransform(date));
138         }
139 
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
145 
146         // get the transform from the current EOP
147         final FieldTransform<T> rawTransform = rawProvider.getTransform(date);
148 
149         // add the conversion layer
150         final ITRFVersion.Converter converterForDate = getConverter(date.toAbsoluteDate());
151         if (converterForDate == null) {
152             return rawTransform;
153         } else {
154             return new FieldTransform<>(date, rawTransform, converterForDate.getTransform(date));
155         }
156 
157     }
158 
159     /** {@inheritDoc} */
160     @Override
161     public <T extends CalculusFieldElement<T>> FieldKinematicTransform<T> getKinematicTransform(final FieldAbsoluteDate<T> date) {
162 
163         // get the transform from the current EOP
164         final FieldKinematicTransform<T> rawTransform = rawProvider.getKinematicTransform(date);
165 
166         // add the conversion layer
167         final ITRFVersion.Converter converterForDate = getConverter(date.toAbsoluteDate());
168         if (converterForDate == null) {
169             return rawTransform;
170         } else {
171             return FieldKinematicTransform.compose(date, rawTransform, converterForDate.getKinematicTransform(date));
172         }
173 
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     public <T extends CalculusFieldElement<T>> FieldStaticTransform<T> getStaticTransform(final FieldAbsoluteDate<T> date) {
179 
180         // get the transform from the current EOP
181         final FieldStaticTransform<T> rawTransform = rawProvider.getStaticTransform(date);
182 
183         // add the conversion layer
184         final ITRFVersion.Converter converterForDate = getConverter(date.toAbsoluteDate());
185         if (converterForDate == null) {
186             return rawTransform;
187         } else {
188             return FieldStaticTransform.compose(
189                     date,
190                     rawTransform,
191                     converterForDate.getStaticTransform(date));
192         }
193 
194     }
195 
196     /** Get a converter for the date.
197      * @param date date to check
198      * @return converter that should be applied for this date, or null
199      * if no converter is needed
200      */
201     private ITRFVersion.Converter getConverter(final AbsoluteDate date) {
202 
203         // check if the current EOP already provides the version we want
204         final ITRFVersion rawVersion = getEOPHistory().getITRFVersion(date);
205         if (rawVersion == version) {
206             // we already have what we need
207             return null;
208         }
209 
210         final ITRFVersion.Converter existing = converter.get();
211         if (existing != null && existing.getOrigin() == rawVersion) {
212             // the current converter can handle this date
213             return existing;
214         }
215 
216         // we need to create a new converter from raw version to desired version
217         final ITRFVersion.Converter newConverter =
218                 ITRFVersion.getConverter(rawVersion, version, tt);
219         converter.compareAndSet(null, newConverter);
220         return newConverter;
221 
222     }
223 
224     /** Replace the instance with a data transfer object for serialization.
225      * @return data transfer object that will be serialized
226      */
227     @DefaultDataContext
228     private Object writeReplace() {
229         return new DataTransferObject(version, rawProvider);
230     }
231 
232     /** Internal class used only for serialization. */
233     @DefaultDataContext
234     private static class DataTransferObject implements Serializable {
235 
236         /** Serializable UID. */
237         private static final long serialVersionUID = 20180403L;
238 
239         /** ITRF version this provider should generate. */
240         private final ITRFVersion version;
241 
242         /** Raw ITRF provider. */
243         private final ITRFProvider rawProvider;
244 
245         /** Simple constructor.
246          * @param version ITRF version this provider should generate
247          * @param rawProvider raw ITRF provider
248          */
249         DataTransferObject(final ITRFVersion version, final ITRFProvider rawProvider) {
250             this.version     = version;
251             this.rawProvider = rawProvider;
252         }
253 
254         /** Replace the deserialized data transfer object with a {@link VersionedITRFProvider}.
255          * @return replacement {@link VersionedITRFProvider}
256          */
257         private Object readResolve() {
258             return new VersionedITRFProvider(version, rawProvider,
259                     DataContext.getDefault().getTimeScales().getTT());
260         }
261 
262     }
263 
264 }