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 AtomicReference<UTCScale> utc = new AtomicReference<>();
51  
52      /** International Atomic Time scale. */
53      private AtomicReference<TAIScale> tai = new AtomicReference<>();
54  
55      /** Terrestrial Time scale. */
56      private AtomicReference<TTScale> tt = new AtomicReference<>();
57  
58      /** Galileo System Time scale. */
59      private AtomicReference<GalileoScale> gst = new AtomicReference<>();
60  
61      /** GLObal NAvigation Satellite System scale. */
62      private AtomicReference<GLONASSScale> glonass = new AtomicReference<>();
63  
64      /** Quasi-Zenith Satellite System scale. */
65      private AtomicReference<QZSSScale> qzss = new AtomicReference<>();
66  
67      /** Global Positioning System scale. */
68      private AtomicReference<GPSScale> gps = new AtomicReference<>();
69  
70      /** Geocentric Coordinate Time scale. */
71      private AtomicReference<TCGScale> tcg = new AtomicReference<>();
72  
73      /** Barycentric Dynamic Time scale. */
74      private AtomicReference<TDBScale> tdb = new AtomicReference<>();
75  
76      /** Barycentric Coordinate Time scale. */
77      private AtomicReference<TCBScale> tcb = new AtomicReference<>();
78  
79      /** IRNSS System Time scale. */
80      private AtomicReference<IRNSSScale> irnss = new AtomicReference<>();
81  
82      /** BDS System Time scale. */
83      private 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="http://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             List<OffsetModel> entries = Collections.emptyList();
181             if (loaders.isEmpty()) {
182                 addDefaultUTCTAIOffsetsLoaders();
183             }
184             for (UTCTAIOffsetsLoader loader : loaders) {
185                 entries = loader.loadOffsets();
186                 if (!entries.isEmpty()) {
187                     break;
188                 }
189             }
190             if (entries.isEmpty()) {
191                 throw new OrekitException(OrekitMessages.NO_IERS_UTC_TAI_HISTORY_DATA_LOADED);
192             }
193             utc.compareAndSet(null, new UTCScale(getTAI(), entries));
194             refUtc = utc.get();
195         }
196 
197         return refUtc;
198 
199     }
200 
201     @Override
202     public UT1Scale getUT1(final IERSConventions conventions, final boolean simpleEOP) {
203         // synchronized to maintain the same semantics as Orekit 10.0
204         synchronized (this) {
205             return super.getUT1(conventions, simpleEOP);
206         }
207     }
208 
209     @Override
210     protected EOPHistory getEopHistory(final IERSConventions conventions,
211                                        final boolean simpleEOP) {
212         return lazyLoadedEop.getEOPHistory(conventions, simpleEOP, this);
213     }
214 
215     // need to make this public for compatibility. Provides access to UT1 constructor.
216     /** {@inheritDoc} */
217     @Override
218     public UT1Scale getUT1(final EOPHistory history) {
219         return super.getUT1(history);
220     }
221 
222     @Override
223     public TTScale getTT() {
224 
225         TTScale refTt = tt.get();
226         if (refTt == null) {
227             tt.compareAndSet(null, new TTScale());
228             refTt = tt.get();
229         }
230 
231         return refTt;
232 
233     }
234 
235     @Override
236     public GalileoScale getGST() {
237 
238         GalileoScale refGst = gst.get();
239         if (refGst == null) {
240             gst.compareAndSet(null, new GalileoScale());
241             refGst = gst.get();
242         }
243 
244         return refGst;
245 
246     }
247 
248     @Override
249     public GLONASSScale getGLONASS() {
250 
251         GLONASSScale refGlonass = glonass.get();
252         if (refGlonass == null) {
253             glonass.compareAndSet(null, new GLONASSScale(getUTC()));
254             refGlonass = glonass.get();
255         }
256 
257         return refGlonass;
258 
259     }
260 
261     @Override
262     public QZSSScale getQZSS() {
263 
264         QZSSScale refQzss = qzss.get();
265         if (refQzss == null) {
266             qzss.compareAndSet(null, new QZSSScale());
267             refQzss = qzss.get();
268         }
269 
270         return refQzss;
271 
272     }
273 
274     @Override
275     public GPSScale getGPS() {
276 
277         GPSScale refGps = gps.get();
278         if (refGps == null) {
279             gps.compareAndSet(null, new GPSScale());
280             refGps = gps.get();
281         }
282 
283         return refGps;
284 
285     }
286 
287     @Override
288     public TCGScale getTCG() {
289 
290         TCGScale refTcg = tcg.get();
291         if (refTcg == null) {
292             tcg.compareAndSet(null, new TCGScale(getTT(), getTAI()));
293             refTcg = tcg.get();
294         }
295 
296         return refTcg;
297 
298     }
299 
300     @Override
301     public TDBScale getTDB() {
302 
303         TDBScale refTdb = tdb.get();
304         if (refTdb == null) {
305             tdb.compareAndSet(null, new TDBScale(getTT(), getJ2000Epoch()));
306             refTdb = tdb.get();
307         }
308 
309         return refTdb;
310 
311     }
312 
313     @Override
314     public TCBScale getTCB() {
315 
316         TCBScale refTcb = tcb.get();
317         if (refTcb == null) {
318             tcb.compareAndSet(null, new TCBScale(getTDB(), getTAI()));
319             refTcb = tcb.get();
320         }
321 
322         return refTcb;
323 
324     }
325 
326     @Override
327     public GMSTScale getGMST(final IERSConventions conventions, final boolean simpleEOP) {
328         // synchronized to maintain the same semantics as Orekit 10.0
329         synchronized (this) {
330             return super.getGMST(conventions, simpleEOP);
331         }
332     }
333 
334     @Override
335     public IRNSSScale getIRNSS() {
336 
337         IRNSSScale refIrnss = irnss.get();
338         if (refIrnss == null) {
339             irnss.compareAndSet(null, new IRNSSScale());
340             refIrnss = irnss.get();
341         }
342 
343         return refIrnss;
344 
345     }
346 
347     @Override
348     public BDTScale getBDT() {
349 
350         BDTScale refBds = bds.get();
351         if (refBds == null) {
352             bds.compareAndSet(null, new BDTScale());
353             refBds = bds.get();
354         }
355 
356         return refBds;
357 
358     }
359 
360 }