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 }