1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.time;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Comparator;
23 import java.util.List;
24
25 import org.hipparchus.RealFieldElement;
26 import org.hipparchus.util.FastMath;
27 import org.orekit.annotation.DefaultDataContext;
28 import org.orekit.data.DataContext;
29 import org.orekit.errors.OrekitException;
30 import org.orekit.errors.OrekitInternalError;
31 import org.orekit.utils.Constants;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class UTCScale implements TimeScale {
51
52
53 private static final long serialVersionUID = 20150402L;
54
55
56 private UTCTAIOffset[] offsets;
57
58
59
60
61
62
63
64
65 UTCScale(final TimeScale tai, final Collection<? extends OffsetModel> offsets) {
66
67 final List<OffsetModel> offsetModels = new ArrayList<>(offsets);
68 offsetModels.sort(Comparator.comparing(OffsetModel::getStart));
69 if (offsetModels.get(0).getStart().getYear() > 1968) {
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 offsetModels.add( 0, new OffsetModel(new DateComponents(1961, 1, 1), 37300, 1.4228180, 0.0012960));
86 offsetModels.add( 1, new OffsetModel(new DateComponents(1961, 8, 1), 37300, 1.3728180, 0.0012960));
87 offsetModels.add( 2, new OffsetModel(new DateComponents(1962, 1, 1), 37665, 1.8458580, 0.0011232));
88 offsetModels.add( 3, new OffsetModel(new DateComponents(1963, 11, 1), 37665, 1.9458580, 0.0011232));
89 offsetModels.add( 4, new OffsetModel(new DateComponents(1964, 1, 1), 38761, 3.2401300, 0.0012960));
90 offsetModels.add( 5, new OffsetModel(new DateComponents(1964, 4, 1), 38761, 3.3401300, 0.0012960));
91 offsetModels.add( 6, new OffsetModel(new DateComponents(1964, 9, 1), 38761, 3.4401300, 0.0012960));
92 offsetModels.add( 7, new OffsetModel(new DateComponents(1965, 1, 1), 38761, 3.5401300, 0.0012960));
93 offsetModels.add( 8, new OffsetModel(new DateComponents(1965, 3, 1), 38761, 3.6401300, 0.0012960));
94 offsetModels.add( 9, new OffsetModel(new DateComponents(1965, 7, 1), 38761, 3.7401300, 0.0012960));
95 offsetModels.add(10, new OffsetModel(new DateComponents(1965, 9, 1), 38761, 3.8401300, 0.0012960));
96 offsetModels.add(11, new OffsetModel(new DateComponents(1966, 1, 1), 39126, 4.3131700, 0.0025920));
97 offsetModels.add(12, new OffsetModel(new DateComponents(1968, 2, 1), 39126, 4.2131700, 0.0025920));
98 }
99
100
101 this.offsets = new UTCTAIOffset[offsetModels.size()];
102
103 UTCTAIOffset previous = null;
104
105
106 for (int i = 0; i < offsetModels.size(); ++i) {
107
108 final OffsetModel o = offsetModels.get(i);
109 final DateComponents date = o.getStart();
110 final int mjdRef = o.getMJDRef();
111 final double offset = o.getOffset();
112 final double slope = o.getSlope();
113
114
115 final double previousOffset = (previous == null) ? 0.0 : previous.getOffset(date, TimeComponents.H00);
116 final AbsoluteDateDate">AbsoluteDate leapStart = new AbsoluteDate(date, tai).shiftedBy(previousOffset);
117
118
119 final double startOffset = offset + slope * (date.getMJD() - mjdRef);
120 final AbsoluteDateDate">AbsoluteDate leapEnd = new AbsoluteDate(date, tai).shiftedBy(startOffset);
121
122
123 final double normalizedSlope = slope / Constants.JULIAN_DAY;
124 final double leap = leapEnd.durationFrom(leapStart) / (1 + normalizedSlope);
125
126 final AbsoluteDate reference = AbsoluteDate.createMJDDate(mjdRef, 0, tai)
127 .shiftedBy(offset);
128 previous = new UTCTAIOffset(leapStart, date.getMJD(), leap, offset, mjdRef,
129 normalizedSlope, reference);
130 this.offsets[i] = previous;
131
132 }
133
134 }
135
136
137 @Override
138 public double offsetFromTAI(final AbsoluteDate date) {
139 final int offsetIndex = findOffsetIndex(date);
140 if (offsetIndex < 0) {
141
142 return 0;
143 } else {
144 return -offsets[offsetIndex].getOffset(date);
145 }
146 }
147
148
149 @Override
150 public <T extends RealFieldElement<T>> T offsetFromTAI(final FieldAbsoluteDate<T> date) {
151 final int offsetIndex = findOffsetIndex(date.toAbsoluteDate());
152 if (offsetIndex < 0) {
153
154 return date.getField().getZero();
155 } else {
156 return offsets[offsetIndex].getOffset(date).negate();
157 }
158 }
159
160
161 @Override
162 public double offsetToTAI(final DateComponents date,
163 final TimeComponents time) {
164
165
166
167
168 final int minuteInDay = time.getHour() * 60 + time.getMinute() - time.getMinutesFromUTC();
169 final int correction = minuteInDay < 0 ? (minuteInDay - 1439) / 1440 : minuteInDay / 1440;
170
171
172 final int mjd = date.getMJD() + correction;
173 final UTCTAIOffset offset = findOffset(mjd);
174 if (offset == null) {
175
176 return 0;
177 } else {
178 return offset.getOffset(date, time);
179 }
180
181 }
182
183
184 public String getName() {
185 return "UTC";
186 }
187
188
189 public String toString() {
190 return getName();
191 }
192
193
194
195
196 public AbsoluteDate getFirstKnownLeapSecond() {
197 return offsets[0].getDate();
198 }
199
200
201
202
203 public AbsoluteDate getLastKnownLeapSecond() {
204 return offsets[offsets.length - 1].getDate();
205 }
206
207
208 @Override
209 public boolean insideLeap(final AbsoluteDate date) {
210 final int offsetIndex = findOffsetIndex(date);
211 if (offsetIndex < 0) {
212
213 return false;
214 } else {
215 return date.compareTo(offsets[offsetIndex].getValidityStart()) < 0;
216 }
217 }
218
219
220 @Override
221 public <T extends RealFieldElement<T>> boolean insideLeap(final FieldAbsoluteDate<T> date) {
222 return insideLeap(date.toAbsoluteDate());
223 }
224
225
226 @Override
227 public int minuteDuration(final AbsoluteDate date) {
228 final int offsetIndex = findOffsetIndex(date);
229 final UTCTAIOffset offset;
230 if (offsetIndex >= 0 &&
231 date.compareTo(offsets[offsetIndex].getValidityStart()) < 0) {
232
233 offset = offsets[offsetIndex];
234 } else if (offsetIndex + 1 < offsets.length &&
235 offsets[offsetIndex + 1].getDate().durationFrom(date) <= 60.0) {
236
237
238 offset = offsets[offsetIndex + 1];
239 } else {
240 offset = null;
241 }
242 if (offset != null) {
243
244
245 return 60 + (int) FastMath.ceil(offset.getLeap());
246 }
247
248 return 60;
249 }
250
251
252 @Override
253 public <T extends RealFieldElement<T>> int minuteDuration(final FieldAbsoluteDate<T> date) {
254 return minuteDuration(date.toAbsoluteDate());
255 }
256
257
258 @Override
259 public double getLeap(final AbsoluteDate date) {
260 final int offsetIndex = findOffsetIndex(date);
261 if (offsetIndex < 0) {
262
263 return 0;
264 } else {
265 return offsets[offsetIndex].getLeap();
266 }
267 }
268
269
270 @Override
271 public <T extends RealFieldElement<T>> T getLeap(final FieldAbsoluteDate<T> date) {
272 return date.getField().getZero().add(getLeap(date.toAbsoluteDate()));
273 }
274
275
276
277
278
279 private int findOffsetIndex(final AbsoluteDate date) {
280 int inf = 0;
281 int sup = offsets.length;
282 while (sup - inf > 1) {
283 final int middle = (inf + sup) >>> 1;
284 if (date.compareTo(offsets[middle].getDate()) < 0) {
285 sup = middle;
286 } else {
287 inf = middle;
288 }
289 }
290 if (sup == offsets.length) {
291
292 return offsets.length - 1;
293 } else if (date.compareTo(offsets[inf].getDate()) < 0) {
294
295 return -1;
296 } else {
297 return inf;
298 }
299 }
300
301
302
303
304
305 private UTCTAIOffset findOffset(final int mjd) {
306 int inf = 0;
307 int sup = offsets.length;
308 while (sup - inf > 1) {
309 final int middle = (inf + sup) >>> 1;
310 if (mjd < offsets[middle].getMJD()) {
311 sup = middle;
312 } else {
313 inf = middle;
314 }
315 }
316 if (sup == offsets.length) {
317
318 return offsets[offsets.length - 1];
319 } else if (mjd < offsets[inf].getMJD()) {
320
321 return null;
322 } else {
323 return offsets[inf];
324 }
325 }
326
327
328
329
330 @DefaultDataContext
331 private Object writeReplace() {
332 return new DataTransferObject();
333 }
334
335
336 @DefaultDataContext
337 private static class DataTransferObject implements Serializable {
338
339
340 private static final long serialVersionUID = 20131209L;
341
342
343
344
345 private Object readResolve() {
346 try {
347 return DataContext.getDefault().getTimeScales().getUTC();
348 } catch (OrekitException oe) {
349 throw new OrekitInternalError(oe);
350 }
351 }
352
353 }
354
355 }