1   /* Copyright 2002-2019 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (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.io.Serializable;
20  
21  import org.hipparchus.RealFieldElement;
22  import org.orekit.utils.Constants;
23  
24  /** Offset between {@link UTCScale UTC} and  {@link TAIScale TAI} time scales.
25   * <p>The {@link UTCScale UTC} and  {@link TAIScale TAI} time scales are two
26   * scales offset with respect to each other. The {@link TAIScale TAI} scale is
27   * continuous whereas the {@link UTCScale UTC} includes some discontinuity when
28   * leap seconds are introduced by the <a href="http://www.iers.org/">International
29   * Earth Rotation Service</a> (IERS).</p>
30   * <p>This class represents the offset between the two scales that is
31   * valid between two leap seconds occurrences. It handles both the linear offsets
32   * used from 1961-01-01 to 1971-12-31 and the constant integer offsets used since
33   * 1972-01-01.</p>
34   * @author Luc Maisonobe
35   * @see UTCScale
36   * @see UTCTAIHistoryFilesLoader
37   */
38  class UTCTAIOffset implements TimeStamped, Serializable {
39  
40      /** Serializable UID. */
41      private static final long serialVersionUID = 4742190573136348054L;
42  
43      /** Leap date. */
44      private final AbsoluteDate leapDate;
45  
46      /** Leap date in Modified Julian Day. */
47      private final int leapDateMJD;
48  
49      /** Offset start of validity date. */
50      private final AbsoluteDate validityStart;
51  
52      /** Reference date for the slope multiplication as Modified Julian Day. */
53      private final int mjdRef;
54  
55      /** Reference date for the slope multiplication. */
56      private final AbsoluteDate reference;
57  
58      /** Value of the leap at offset validity start (in seconds). */
59      private final double leap;
60  
61      /** Offset at validity start in seconds (TAI minus UTC). */
62      private final double offset;
63  
64      /** Offset slope in seconds per UTC second (TAI minus UTC / dUTC). */
65      private final double slopeUTC;
66  
67      /** Offset slope in seconds per TAI second (TAI minus UTC / dTAI). */
68      private final double slopeTAI;
69  
70      /** Simple constructor for a constant model.
71       * @param leapDate leap date
72       * @param leapDateMJD leap date in Modified Julian Day
73       * @param leap value of the leap at offset validity start (in seconds)
74       * @param offset offset in seconds (TAI minus UTC)
75       */
76      UTCTAIOffset(final AbsoluteDate leapDate, final int leapDateMJD,
77                          final double leap, final double offset) {
78          this(leapDate, leapDateMJD, leap, offset, 0, 0);
79      }
80  
81      /** Simple constructor for a linear model.
82       * @param leapDate leap date
83       * @param leapDateMJD leap date in Modified Julian Day
84       * @param leap value of the leap at offset validity start (in seconds)
85       * @param offset offset in seconds (TAI minus UTC)
86       * @param mjdRef reference date for the slope multiplication as Modified Julian Day
87       * @param slope offset slope in seconds per UTC second (TAI minus UTC / dUTC)
88       */
89      UTCTAIOffset(final AbsoluteDate leapDate, final int leapDateMJD,
90                          final double leap, final double offset,
91                          final int mjdRef, final double slope) {
92          this.leapDate      = leapDate;
93          this.leapDateMJD   = leapDateMJD;
94          this.validityStart = leapDate.shiftedBy(leap);
95          this.mjdRef        = mjdRef;
96          this.reference     = new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjdRef),
97                                                TimeScalesFactory.getTAI()).shiftedBy(offset);
98          this.leap          = leap;
99          this.offset        = offset;
100         this.slopeUTC      = slope;
101         this.slopeTAI      = slope / (1 + slope);
102     }
103 
104     /** Get the date of the start of the leap.
105      * @return date of the start of the leap
106      * @see #getValidityStart()
107      */
108     public AbsoluteDate getDate() {
109         return leapDate;
110     }
111 
112     /** Get the date of the start of the leap as Modified Julian Day.
113      * @return date of the start of the leap as Modified Julian Day
114      */
115     public int getMJD() {
116         return leapDateMJD;
117     }
118 
119     /** Get the start time of validity for this offset.
120      * <p>The start of the validity of the offset is {@link #getLeap()}
121      * seconds after the start of the leap itself.</p>
122      * @return start of validity date
123      * @see #getDate()
124      */
125     public AbsoluteDate getValidityStart() {
126         return validityStart;
127     }
128 
129     /** Get the value of the leap at offset validity start (in seconds).
130      * @return value of the leap at offset validity start (in seconds)
131      */
132     public double getLeap() {
133         return leap;
134     }
135 
136     /** Get the TAI - UTC offset in seconds.
137      * @param date date at which the offset is requested
138      * @return TAI - UTC offset in seconds.
139      */
140     public double getOffset(final AbsoluteDate date) {
141         if (slopeTAI == 0) {
142             // we use an if statement here so the offset computation returns
143             // a finite value when date is AbsoluteDate.FUTURE_INFINITY
144             // without this if statement, the multiplication between an
145             // infinite duration and a zero slope would induce a NaN offset
146             return offset;
147         } else {
148             return offset + date.durationFrom(reference) * slopeTAI;
149         }
150     }
151 
152     /** Get the TAI - UTC offset in seconds.
153      * @param date date at which the offset is requested
154      * @param <T> type of the filed elements
155      * @return TAI - UTC offset in seconds.
156      * @since 9.0
157      */
158     public <T extends RealFieldElement<T>> T getOffset(final FieldAbsoluteDate<T> date) {
159         if (slopeTAI == 0) {
160             // we use an if statement here so the offset computation returns
161             // a finite value when date is FieldAbsoluteDate.getFutureInfinity(field)
162             // without this if statement, the multiplication between an
163             // infinite duration and a zero slope would induce a NaN offset
164             return date.getField().getZero().add(offset);
165         } else {
166             return date.durationFrom(reference).multiply(slopeTAI).add(offset);
167         }
168     }
169 
170     /** Get the TAI - UTC offset in seconds.
171      * @param date date components (in UTC) at which the offset is requested
172      * @param time time components (in UTC) at which the offset is requested
173      * @return TAI - UTC offset in seconds.
174      */
175     public double getOffset(final DateComponents date, final TimeComponents time) {
176         final int    days     = date.getMJD() - mjdRef;
177         final double fraction = time.getSecondsInUTCDay();
178         return offset + days * (slopeUTC * Constants.JULIAN_DAY) + fraction * slopeUTC;
179     }
180 
181 }