1   /* Contributed in the public domain.
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.time;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.concurrent.atomic.AtomicReference;
23  
24  import org.orekit.data.DataProvidersManager;
25  import org.orekit.errors.OrekitException;
26  import org.orekit.errors.OrekitMessages;
27  import org.orekit.frames.EOPHistory;
28  import org.orekit.frames.LazyLoadedEop;
29  import org.orekit.utils.IERSConventions;
30  
31  /**
32   * An implementation of {@link TimeScales} that loads auxiliary data, leap seconds and
33   * UT1-UTC, when it is first accessed. The list of loaders may be modified before the
34   * first data access.
35   *
36   * @author Luc Maisonobe
37   * @author Evan Ward
38   * @see TimeScalesFactory
39   * @since 10.1
40   */
41  public class LazyLoadedTimeScales extends AbstractTimeScales {
42  
43      /** Source of EOP data. */
44      private final LazyLoadedEop lazyLoadedEop;
45  
46      /** UTCTAI offsets loaders. */
47      private final List<UTCTAIOffsetsLoader> loaders = new ArrayList<>();
48  
49      /** Universal Time Coordinate scale. */
50      private final AtomicReference<UTCScale> utc = new AtomicReference<>();
51  
52      /** International Atomic Time scale. */
53      private final AtomicReference<TAIScale> tai = new AtomicReference<>();
54  
55      /** Terrestrial Time scale. */
56      private final AtomicReference<TTScale> tt = new AtomicReference<>();
57  
58      /** Galileo System Time scale. */
59      private final AtomicReference<GalileoScale> gst = new AtomicReference<>();
60  
61      /** GLObal NAvigation Satellite System scale. */
62      private final AtomicReference<GLONASSScale> glonass = new AtomicReference<>();
63  
64      /** Quasi-Zenith Satellite System scale. */
65      private final AtomicReference<QZSSScale> qzss = new AtomicReference<>();
66  
67      /** Global Positioning System scale. */
68      private final AtomicReference<GPSScale> gps = new AtomicReference<>();
69  
70      /** Geocentric Coordinate Time scale. */
71      private final AtomicReference<TCGScale> tcg = new AtomicReference<>();
72  
73      /** Barycentric Dynamic Time scale. */
74      private final AtomicReference<TDBScale> tdb = new AtomicReference<>();
75  
76      /** Barycentric Coordinate Time scale. */
77      private final AtomicReference<TCBScale> tcb = new AtomicReference<>();
78  
79      /** NavIC System Time scale. */
80      private final AtomicReference<NavicScale> navic = new AtomicReference<>();
81  
82      /** BDS System Time scale. */
83      private final AtomicReference<BDTScale> bds = new AtomicReference<>();
84  
85      /**
86       * Create a new set of time scales with the given sources of auxiliary data. This
87       * constructor uses the same {@link DataProvidersManager} for the default EOP loaders
88       * and the default leap second loaders.
89       *
90       * @param lazyLoadedEop loads Earth Orientation Parameters for {@link
91       *                      #getUT1(IERSConventions, boolean)}.
92       */
93      public LazyLoadedTimeScales(final LazyLoadedEop lazyLoadedEop) {
94          this.lazyLoadedEop = lazyLoadedEop;
95      }
96  
97      /**
98       * Add a loader for UTC-TAI offsets history files.
99       *
100      * @param loader custom loader to add
101      * @see TAIUTCDatFilesLoader
102      * @see UTCTAIHistoryFilesLoader
103      * @see UTCTAIBulletinAFilesLoader
104      * @see #getUTC()
105      * @see #clearUTCTAIOffsetsLoaders()
106      * @since 7.1
107      */
108     public void addUTCTAIOffsetsLoader(final UTCTAIOffsetsLoader loader) {
109         synchronized (this) {
110             loaders.add(loader);
111         }
112     }
113 
114     /**
115      * Add the default loaders for UTC-TAI offsets history files (both IERS and USNO).
116      * <p>
117      * The default loaders are {@link TAIUTCDatFilesLoader} that looks for a file named
118      * {@code tai-utc.dat} that must be in USNO format, {@link
119      * UTCTAIHistoryFilesLoader} that looks for a file named {@code UTC-TAI.history} that
120      * must be in the IERS format and {@link AGILeapSecondFilesLoader} that looks for a
121      * files named {@code LeapSecond.dat} that must be in AGI format. The {@link
122      * UTCTAIBulletinAFilesLoader} is<em>not</em> added by default as it is not recommended.
123      * USNO warned us that the TAI-UTC data present in bulletin A was for convenience only
124      * and was not reliable, there have been errors in several bulletins regarding these data.
125      * </p>
126      *
127      * @see <a href="http://maia.usno.navy.mil/ser7/tai-utc.dat">USNO tai-utc.dat
128      * file</a>
129      * @see <a href="https://hpiers.obspm.fr/eoppc/bul/bulc/UTC-TAI.history">IERS
130      * UTC-TAI.history file</a>
131      * @see TAIUTCDatFilesLoader
132      * @see UTCTAIHistoryFilesLoader
133      * @see AGILeapSecondFilesLoader
134      * @see #getUTC()
135      * @see #clearUTCTAIOffsetsLoaders()
136      * @since 7.1
137      */
138     public void addDefaultUTCTAIOffsetsLoaders() {
139         synchronized (this) {
140             final DataProvidersManager dataProvidersManager =
141                     lazyLoadedEop.getDataProvidersManager();
142             addUTCTAIOffsetsLoader(new TAIUTCDatFilesLoader(TAIUTCDatFilesLoader.DEFAULT_SUPPORTED_NAMES, dataProvidersManager));
143             addUTCTAIOffsetsLoader(new UTCTAIHistoryFilesLoader(dataProvidersManager));
144             addUTCTAIOffsetsLoader(new AGILeapSecondFilesLoader(AGILeapSecondFilesLoader.DEFAULT_SUPPORTED_NAMES, dataProvidersManager));
145         }
146     }
147 
148     /**
149      * Clear loaders for UTC-TAI offsets history files.
150      *
151      * @see #getUTC()
152      * @see #addUTCTAIOffsetsLoader(UTCTAIOffsetsLoader)
153      * @see #addDefaultUTCTAIOffsetsLoaders()
154      * @since 7.1
155      */
156     public void clearUTCTAIOffsetsLoaders() {
157         synchronized (this) {
158             loaders.clear();
159         }
160     }
161 
162     @Override
163     public TAIScale getTAI() {
164 
165         TAIScale refTai = tai.get();
166         if (refTai == null) {
167             tai.compareAndSet(null, new TAIScale());
168             refTai = tai.get();
169         }
170 
171         return refTai;
172 
173     }
174 
175     @Override
176     public UTCScale getUTC() {
177 
178         UTCScale refUtc = utc.get();
179         if (refUtc == null) {
180             synchronized (this) {
181                 if (utc.get() == null) { // Check if utc was not loaded in the meantime
182                     List<OffsetModel> entries = Collections.emptyList();
183                     if (loaders.isEmpty()) {
184                         addDefaultUTCTAIOffsetsLoaders();
185                     }
186                     for (UTCTAIOffsetsLoader loader : loaders) {
187                         entries = loader.loadOffsets();
188                         if (!entries.isEmpty()) {
189                             break;
190                         }
191                     }
192                     if (entries.isEmpty()) {
193                         throw new OrekitException(OrekitMessages.NO_IERS_UTC_TAI_HISTORY_DATA_LOADED);
194                     }
195                     utc.compareAndSet(null, new UTCScale(getTAI(), entries));
196                 }
197                 refUtc = utc.get();
198             }
199         }
200 
201         return refUtc;
202 
203     }
204 
205     @Override
206     public UT1Scale getUT1(final IERSConventions conventions, final boolean simpleEOP) {
207         // synchronized to maintain the same semantics as Orekit 10.0
208         synchronized (this) {
209             return super.getUT1(conventions, simpleEOP);
210         }
211     }
212 
213     @Override
214     protected EOPHistory getEopHistory(final IERSConventions conventions,
215                                        final boolean simpleEOP) {
216         return lazyLoadedEop.getEOPHistory(conventions, simpleEOP, this);
217     }
218 
219     // need to make this public for compatibility. Provides access to UT1 constructor.
220     /** {@inheritDoc} */
221     @Override
222     public UT1Scale getUT1(final EOPHistory history) {
223         return super.getUT1(history);
224     }
225 
226     @Override
227     public TTScale getTT() {
228 
229         TTScale refTt = tt.get();
230         if (refTt == null) {
231             tt.compareAndSet(null, new TTScale());
232             refTt = tt.get();
233         }
234 
235         return refTt;
236 
237     }
238 
239     @Override
240     public GalileoScale getGST() {
241 
242         GalileoScale refGst = gst.get();
243         if (refGst == null) {
244             gst.compareAndSet(null, new GalileoScale());
245             refGst = gst.get();
246         }
247 
248         return refGst;
249 
250     }
251 
252     @Override
253     public GLONASSScale getGLONASS() {
254 
255         GLONASSScale refGlonass = glonass.get();
256         if (refGlonass == null) {
257             glonass.compareAndSet(null, new GLONASSScale(getUTC()));
258             refGlonass = glonass.get();
259         }
260 
261         return refGlonass;
262 
263     }
264 
265     @Override
266     public QZSSScale getQZSS() {
267 
268         QZSSScale refQzss = qzss.get();
269         if (refQzss == null) {
270             qzss.compareAndSet(null, new QZSSScale());
271             refQzss = qzss.get();
272         }
273 
274         return refQzss;
275 
276     }
277 
278     @Override
279     public GPSScale getGPS() {
280 
281         GPSScale refGps = gps.get();
282         if (refGps == null) {
283             gps.compareAndSet(null, new GPSScale());
284             refGps = gps.get();
285         }
286 
287         return refGps;
288 
289     }
290 
291     @Override
292     public TCGScale getTCG() {
293 
294         TCGScale refTcg = tcg.get();
295         if (refTcg == null) {
296             tcg.compareAndSet(null, new TCGScale(getTT(), getTAI()));
297             refTcg = tcg.get();
298         }
299 
300         return refTcg;
301 
302     }
303 
304     @Override
305     public TDBScale getTDB() {
306 
307         TDBScale refTdb = tdb.get();
308         if (refTdb == null) {
309             tdb.compareAndSet(null, new TDBScale(getTT(), getJ2000Epoch()));
310             refTdb = tdb.get();
311         }
312 
313         return refTdb;
314 
315     }
316 
317     @Override
318     public TCBScale getTCB() {
319 
320         TCBScale refTcb = tcb.get();
321         if (refTcb == null) {
322             tcb.compareAndSet(null, new TCBScale(getTDB(), getTAI()));
323             refTcb = tcb.get();
324         }
325 
326         return refTcb;
327 
328     }
329 
330     @Override
331     public GMSTScale getGMST(final IERSConventions conventions, final boolean simpleEOP) {
332         // synchronized to maintain the same semantics as Orekit 10.0
333         synchronized (this) {
334             return super.getGMST(conventions, simpleEOP);
335         }
336     }
337 
338     @Override
339     public NavicScale getNavIC() {
340 
341         NavicScale refNavIC = navic.get();
342         if (refNavIC == null) {
343             navic.compareAndSet(null, new NavicScale());
344             refNavIC = navic.get();
345         }
346 
347         return refNavIC;
348 
349     }
350 
351     @Override
352     public BDTScale getBDT() {
353 
354         BDTScale refBds = bds.get();
355         if (refBds == null) {
356             bds.compareAndSet(null, new BDTScale());
357             refBds = bds.get();
358         }
359 
360         return refBds;
361 
362     }
363 
364 }