1   /* Copyright 2002-2015 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  import java.util.Date;
21  
22  import org.apache.commons.math3.util.FastMath;
23  import org.apache.commons.math3.util.MathArrays;
24  import org.orekit.errors.OrekitException;
25  import org.orekit.errors.OrekitMessages;
26  import org.orekit.utils.Constants;
27  
28  
29  /** This class represents a specific instant in time.
30  
31   * <p>Instances of this class are considered to be absolute in the sense
32   * that each one represent the occurrence of some event and can be compared
33   * to other instances or located in <em>any</em> {@link TimeScale time scale}. In
34   * other words the different locations of an event with respect to two different
35   * time scales (say {@link TAIScale TAI} and {@link UTCScale UTC} for example) are
36   * simply different perspective related to a single object. Only one
37   * <code>AbsoluteDate</code> instance is needed, both representations being available
38   * from this single instance by specifying the time scales as parameter when calling
39   * the ad-hoc methods.</p>
40   *
41   * <p>Since an instance is not bound to a specific time-scale, all methods related
42   * to the location of the date within some time scale require to provide the time
43   * scale as an argument. It is therefore possible to define a date in one time scale
44   * and to use it in another one. An example of such use is to read a date from a file
45   * in UTC and write it in another file in TAI. This can be done as follows:</p>
46   * <pre>
47   *   DateTimeComponents utcComponents = readNextDate();
48   *   AbsoluteDate date = new AbsoluteDate(utcComponents, TimeScalesFactory.getUTC());
49   *   writeNextDate(date.getComponents(TimeScalesFactory.getTAI()));
50   * </pre>
51   *
52   * <p>Two complementary views are available:</p>
53   * <ul>
54   *   <li><p>location view (mainly for input/output or conversions)</p>
55   *   <p>locations represent the coordinate of one event with respect to a
56   *   {@link TimeScale time scale}. The related methods are {@link
57   *   #AbsoluteDate(DateComponents, TimeComponents, TimeScale)}, {@link
58   *   #AbsoluteDate(int, int, int, int, int, double, TimeScale)}, {@link
59   *   #AbsoluteDate(int, int, int, TimeScale)}, {@link #AbsoluteDate(Date,
60   *   TimeScale)}, {@link #createGPSDate(int, double)}, {@link
61   *   #parseCCSDSCalendarSegmentedTimeCode(byte, byte[])}, toString(){@link
62   *   #toDate(TimeScale)}, {@link #toString(TimeScale) toString(timeScale)},
63   *   {@link #toString()}, and {@link #timeScalesOffset}.</p>
64   *   </li>
65   *   <li><p>offset view (mainly for physical computation)</p>
66   *   <p>offsets represent either the flow of time between two events
67   *   (two instances of the class) or durations. They are counted in seconds,
68   *   are continuous and could be measured using only a virtually perfect stopwatch.
69   *   The related methods are {@link #AbsoluteDate(AbsoluteDate, double)},
70   *   {@link #parseCCSDSUnsegmentedTimeCode(byte, byte, byte[], AbsoluteDate)},
71   *   {@link #parseCCSDSDaySegmentedTimeCode(byte, byte[], DateComponents)},
72   *   {@link #durationFrom(AbsoluteDate)}, {@link #compareTo(AbsoluteDate)}, {@link #equals(Object)}
73   *   and {@link #hashCode()}.</p>
74   *   </li>
75   * </ul>
76   * <p>
77   * A few reference epochs which are commonly used in space systems have been defined. These
78   * epochs can be used as the basis for offset computation. The supported epochs are:
79   * {@link #JULIAN_EPOCH}, {@link #MODIFIED_JULIAN_EPOCH}, {@link #FIFTIES_EPOCH},
80   * {@link #CCSDS_EPOCH}, {@link #GALILEO_EPOCH}, {@link #GPS_EPOCH}, {@link #J2000_EPOCH},
81   * {@link #JAVA_EPOCH}. There are also two factory methods {@link #createJulianEpoch(double)}
82   * and {@link #createBesselianEpoch(double)} that can be used to compute other reference
83   * epochs like J1900.0 or B1950.0.
84   * In addition to these reference epochs, two other constants are defined for convenience:
85   * {@link #PAST_INFINITY} and {@link #FUTURE_INFINITY}, which can be used either as dummy
86   * dates when a date is not yet initialized, or for initialization of loops searching for
87   * a min or max date.
88   * </p>
89   * <p>
90   * Instances of the <code>AbsoluteDate</code> class are guaranteed to be immutable.
91   * </p>
92   * @author Luc Maisonobe
93   * @see TimeScale
94   * @see TimeStamped
95   * @see ChronologicalComparator
96   */
97  public class AbsoluteDate
98      implements TimeStamped, TimeShiftable<AbsoluteDate>, Comparable<AbsoluteDate>, Serializable {
99  
100     /** Reference epoch for julian dates: -4712-01-01T12:00:00 Terrestrial Time.
101      * <p>Both <code>java.util.Date</code> and {@link DateComponents} classes
102      * follow the astronomical conventions and consider a year 0 between
103      * years -1 and +1, hence this reference date lies in year -4712 and not
104      * in year -4713 as can be seen in other documents or programs that obey
105      * a different convention (for example the <code>convcal</code> utility).</p>
106      */
107     public static final AbsoluteDate JULIAN_EPOCH =
108         new AbsoluteDate(DateComponents.JULIAN_EPOCH, TimeComponents.H12, TimeScalesFactory.getTT());
109 
110     /** Reference epoch for modified julian dates: 1858-11-17T00:00:00 Terrestrial Time. */
111     public static final AbsoluteDate MODIFIED_JULIAN_EPOCH =
112         new AbsoluteDate(DateComponents.MODIFIED_JULIAN_EPOCH, TimeComponents.H00, TimeScalesFactory.getTT());
113 
114     /** Reference epoch for 1950 dates: 1950-01-01T00:00:00 Terrestrial Time. */
115     public static final AbsoluteDate FIFTIES_EPOCH =
116         new AbsoluteDate(DateComponents.FIFTIES_EPOCH, TimeComponents.H00, TimeScalesFactory.getTT());
117 
118     /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4):
119      * 1958-01-01T00:00:00 International Atomic Time (<em>not</em> UTC). */
120     public static final AbsoluteDate CCSDS_EPOCH =
121         new AbsoluteDate(DateComponents.CCSDS_EPOCH, TimeComponents.H00, TimeScalesFactory.getTAI());
122 
123     /** Reference epoch for Galileo System Time: 1999-08-22T00:00:00 UTC. */
124     public static final AbsoluteDate GALILEO_EPOCH =
125         new AbsoluteDate(DateComponents.GALILEO_EPOCH, new TimeComponents(0, 0, 32),
126                          TimeScalesFactory.getTAI());
127 
128     /** Reference epoch for GPS weeks: 1980-01-06T00:00:00 GPS time. */
129     public static final AbsoluteDate GPS_EPOCH =
130         new AbsoluteDate(DateComponents.GPS_EPOCH, TimeComponents.H00, TimeScalesFactory.getGPS());
131 
132     /** J2000.0 Reference epoch: 2000-01-01T12:00:00 Terrestrial Time (<em>not</em> UTC).
133      * @see #createJulianEpoch(double)
134      * @see #createBesselianEpoch(double)
135      */
136     public static final AbsoluteDate J2000_EPOCH =
137         new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTT());
138 
139     /** Java Reference epoch: 1970-01-01T00:00:00 Universal Time Coordinate.
140      * <p>
141      * Between 1968-02-01 and 1972-01-01, UTC-TAI = 4.213 170 0s + (MJD - 39 126) x 0.002 592s.
142      * As on 1970-01-01 MJD = 40587, UTC-TAI = 8.000082s
143      * </p>
144      */
145     public static final AbsoluteDate JAVA_EPOCH =
146         new AbsoluteDate(DateComponents.JAVA_EPOCH, TimeScalesFactory.getTAI()).shiftedBy(8.000082);
147 
148     /** Dummy date at infinity in the past direction. */
149     public static final AbsoluteDate PAST_INFINITY = JAVA_EPOCH.shiftedBy(Double.NEGATIVE_INFINITY);
150 
151     /** Dummy date at infinity in the future direction. */
152     public static final AbsoluteDate FUTURE_INFINITY = JAVA_EPOCH.shiftedBy(Double.POSITIVE_INFINITY);
153 
154     /** Serializable UID. */
155     private static final long serialVersionUID = 617061803741806846L;
156 
157     /** Reference epoch in seconds from 2000-01-01T12:00:00 TAI.
158      * <p>Beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT.</p> */
159     private final long epoch;
160 
161     /** Offset from the reference epoch in seconds. */
162     private final double offset;
163 
164     /** Create an instance with a default value ({@link #J2000_EPOCH}).
165      */
166     public AbsoluteDate() {
167         epoch  = J2000_EPOCH.epoch;
168         offset = J2000_EPOCH.offset;
169     }
170 
171     /** Build an instance from a location (parsed from a string) in a {@link TimeScale time scale}.
172      * <p>
173      * The supported formats for location are mainly the ones defined in ISO-8601 standard,
174      * the exact subset is explained in {@link DateTimeComponents#parseDateTime(String)},
175      * {@link DateComponents#parseDate(String)} and {@link TimeComponents#parseTime(String)}.
176      * </p>
177      * <p>
178      * As CCSDS ASCII calendar segmented time code is a trimmed down version of ISO-8601,
179      * it is also supported by this constructor.
180      * </p>
181      * @param location location in the time scale, must be in a supported format
182      * @param timeScale time scale
183      * @exception IllegalArgumentException if location string is not in a supported format
184      */
185     public AbsoluteDate(final String location, final TimeScale timeScale) {
186         this(DateTimeComponents.parseDateTime(location), timeScale);
187     }
188 
189     /** Build an instance from a location in a {@link TimeScale time scale}.
190      * @param location location in the time scale
191      * @param timeScale time scale
192      */
193     public AbsoluteDate(final DateTimeComponents location, final TimeScale timeScale) {
194         this(location.getDate(), location.getTime(), timeScale);
195     }
196 
197     /** Build an instance from a location in a {@link TimeScale time scale}.
198      * @param date date location in the time scale
199      * @param time time location in the time scale
200      * @param timeScale time scale
201      */
202     public AbsoluteDate(final DateComponents date, final TimeComponents time,
203                         final TimeScale timeScale) {
204 
205         final double seconds  = time.getSecond();
206         final double tsOffset = timeScale.offsetToTAI(date, time);
207 
208         // compute sum exactly, using Møller-Knuth TwoSum algorithm without branching
209         // the following statements must NOT be simplified, they rely on floating point
210         // arithmetic properties (rounding and representable numbers)
211         // at the end, the EXACT result of addition seconds + tsOffset
212         // is sum + residual, where sum is the closest representable number to the exact
213         // result and residual is the missing part that does not fit in the first number
214         final double sum      = seconds + tsOffset;
215         final double sPrime   = sum - tsOffset;
216         final double tPrime   = sum - sPrime;
217         final double deltaS   = seconds  - sPrime;
218         final double deltaT   = tsOffset - tPrime;
219         final double residual = deltaS   + deltaT;
220         final long   dl       = (long) FastMath.floor(sum);
221 
222         offset = (sum - dl) + residual;
223         epoch  = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l + time.getMinute() - 720l) + dl;
224 
225     }
226 
227     /** Build an instance from a location in a {@link TimeScale time scale}.
228      * @param year year number (may be 0 or negative for BC years)
229      * @param month month number from 1 to 12
230      * @param day day number from 1 to 31
231      * @param hour hour number from 0 to 23
232      * @param minute minute number from 0 to 59
233      * @param second second number from 0.0 to 60.0 (excluded)
234      * @param timeScale time scale
235      * @exception IllegalArgumentException if inconsistent arguments
236      * are given (parameters out of range)
237      */
238     public AbsoluteDate(final int year, final int month, final int day,
239                         final int hour, final int minute, final double second,
240                         final TimeScale timeScale) throws IllegalArgumentException {
241         this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
242     }
243 
244     /** Build an instance from a location in a {@link TimeScale time scale}.
245      * @param year year number (may be 0 or negative for BC years)
246      * @param month month enumerate
247      * @param day day number from 1 to 31
248      * @param hour hour number from 0 to 23
249      * @param minute minute number from 0 to 59
250      * @param second second number from 0.0 to 60.0 (excluded)
251      * @param timeScale time scale
252      * @exception IllegalArgumentException if inconsistent arguments
253      * are given (parameters out of range)
254      */
255     public AbsoluteDate(final int year, final Month month, final int day,
256                         final int hour, final int minute, final double second,
257                         final TimeScale timeScale) throws IllegalArgumentException {
258         this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
259     }
260 
261     /** Build an instance from a location in a {@link TimeScale time scale}.
262      * <p>The hour is set to 00:00:00.000.</p>
263      * @param date date location in the time scale
264      * @param timeScale time scale
265      * @exception IllegalArgumentException if inconsistent arguments
266      * are given (parameters out of range)
267      */
268     public AbsoluteDate(final DateComponents date, final TimeScale timeScale)
269         throws IllegalArgumentException {
270         this(date, TimeComponents.H00, timeScale);
271     }
272 
273     /** Build an instance from a location in a {@link TimeScale time scale}.
274      * <p>The hour is set to 00:00:00.000.</p>
275      * @param year year number (may be 0 or negative for BC years)
276      * @param month month number from 1 to 12
277      * @param day day number from 1 to 31
278      * @param timeScale time scale
279      * @exception IllegalArgumentException if inconsistent arguments
280      * are given (parameters out of range)
281      */
282     public AbsoluteDate(final int year, final int month, final int day,
283                         final TimeScale timeScale) throws IllegalArgumentException {
284         this(new DateComponents(year, month, day), TimeComponents.H00, timeScale);
285     }
286 
287     /** Build an instance from a location in a {@link TimeScale time scale}.
288      * <p>The hour is set to 00:00:00.000.</p>
289      * @param year year number (may be 0 or negative for BC years)
290      * @param month month enumerate
291      * @param day day number from 1 to 31
292      * @param timeScale time scale
293      * @exception IllegalArgumentException if inconsistent arguments
294      * are given (parameters out of range)
295      */
296     public AbsoluteDate(final int year, final Month month, final int day,
297                         final TimeScale timeScale) throws IllegalArgumentException {
298         this(new DateComponents(year, month, day), TimeComponents.H00, timeScale);
299     }
300 
301     /** Build an instance from a location in a {@link TimeScale time scale}.
302      * @param location location in the time scale
303      * @param timeScale time scale
304      */
305     public AbsoluteDate(final Date location, final TimeScale timeScale) {
306         this(new DateComponents(DateComponents.JAVA_EPOCH,
307                                 (int) (location.getTime() / 86400000l)),
308                                 new TimeComponents(0.001 * (location.getTime() % 86400000l)),
309              timeScale);
310     }
311 
312     /** Build an instance from an elapsed duration since to another instant.
313      * <p>It is important to note that the elapsed duration is <em>not</em>
314      * the difference between two readings on a time scale. As an example,
315      * the duration between the two instants leading to the readings
316      * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC}
317      * time scale is <em>not</em> 1 second, but a stop watch would have measured
318      * an elapsed duration of 2 seconds between these two instances because a leap
319      * second was introduced at the end of 2005 in this time scale.</p>
320      * <p>This constructor is the reverse of the {@link #durationFrom(AbsoluteDate)}
321      * method.</p>
322      * @param since start instant of the measured duration
323      * @param elapsedDuration physically elapsed duration from the <code>since</code>
324      * instant, as measured in a regular time scale
325      * @see #durationFrom(AbsoluteDate)
326      */
327     public AbsoluteDate(final AbsoluteDate since, final double elapsedDuration) {
328 
329         final double sum = since.offset + elapsedDuration;
330         if (Double.isInfinite(sum)) {
331             offset = sum;
332             epoch  = (sum < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
333         } else {
334             // compute sum exactly, using Møller-Knuth TwoSum algorithm without branching
335             // the following statements must NOT be simplified, they rely on floating point
336             // arithmetic properties (rounding and representable numbers)
337             // at the end, the EXACT result of addition since.offset + elapsedDuration
338             // is sum + residual, where sum is the closest representable number to the exact
339             // result and residual is the missing part that does not fit in the first number
340             final double oPrime   = sum - elapsedDuration;
341             final double dPrime   = sum - oPrime;
342             final double deltaO   = since.offset - oPrime;
343             final double deltaD   = elapsedDuration - dPrime;
344             final double residual = deltaO + deltaD;
345             final long   dl       = (long) FastMath.floor(sum);
346             offset = (sum - dl) + residual;
347             epoch  = since.epoch  + dl;
348         }
349     }
350 
351     /** Build an instance from an apparent clock offset with respect to another
352      * instant <em>in the perspective of a specific {@link TimeScale time scale}</em>.
353      * <p>It is important to note that the apparent clock offset <em>is</em> the
354      * difference between two readings on a time scale and <em>not</em> an elapsed
355      * duration. As an example, the apparent clock offset between the two instants
356      * leading to the readings 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the
357      * {@link UTCScale UTC} time scale is 1 second, but the elapsed duration is 2
358      * seconds because a leap second has been introduced at the end of 2005 in this
359      * time scale.</p>
360      * <p>This constructor is the reverse of the {@link #offsetFrom(AbsoluteDate,
361      * TimeScale)} method.</p>
362      * @param reference reference instant
363      * @param apparentOffset apparent clock offset from the reference instant
364      * (difference between two readings in the specified time scale)
365      * @param timeScale time scale with respect to which the offset is defined
366      * @see #offsetFrom(AbsoluteDate, TimeScale)
367      */
368     public AbsoluteDate(final AbsoluteDate reference, final double apparentOffset,
369                         final TimeScale timeScale) {
370         this(new DateTimeComponents(reference.getComponents(timeScale), apparentOffset),
371              timeScale);
372     }
373 
374     /** Build an instance from a CCSDS Unsegmented Time Code (CUC).
375      * <p>
376      * CCSDS Unsegmented Time Code is defined in the blue book:
377      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
378      * </p>
379      * <p>
380      * If the date to be parsed is formatted using version 3 of the standard
381      * (CCSDS 301.0-B-3 published in 2002) or if the extension of the preamble
382      * field introduced in version 4 of the standard is not used, then the
383      * {@code preambleField2} parameter can be set to 0.
384      * </p>
385      * @param preambleField1 first byte of the field specifying the format, often
386      * not transmitted in data interfaces, as it is constant for a given data interface
387      * @param preambleField2 second byte of the field specifying the format
388      * (added in revision 4 of the CCSDS standard in 2010), often not transmitted in data
389      * interfaces, as it is constant for a given data interface (value ignored if presence
390      * not signaled in {@code preambleField1})
391      * @param timeField byte array containing the time code
392      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
393      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
394      * may be null in this case)
395      * @return an instance corresponding to the specified date
396      * @throws OrekitException if preamble is inconsistent with Unsegmented Time Code,
397      * or if it is inconsistent with time field, or if agency epoch is needed but not provided
398      */
399     public static AbsoluteDate parseCCSDSUnsegmentedTimeCode(final byte preambleField1,
400                                                              final byte preambleField2,
401                                                              final byte[] timeField,
402                                                              final AbsoluteDate agencyDefinedEpoch)
403         throws OrekitException {
404 
405         // time code identification and reference epoch
406         final AbsoluteDate epoch;
407         switch (preambleField1 & 0x70) {
408         case 0x10:
409             // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
410             epoch = CCSDS_EPOCH;
411             break;
412         case 0x20:
413             // the reference epoch is agency defined
414             if (agencyDefinedEpoch == null) {
415                 throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
416             }
417             epoch = agencyDefinedEpoch;
418             break;
419         default :
420             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
421                                       formatByte(preambleField1));
422         }
423 
424         // time field lengths
425         int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2);
426         int fineTimeLength   = preambleField1 & 0x03;
427 
428         if ((preambleField1 & 0x80) != 0x0) {
429             // there is an additional octet in preamble field
430             coarseTimeLength += (preambleField2 & 0x60) >>> 5;
431             fineTimeLength   += (preambleField2 & 0x1C) >>> 2;
432         }
433 
434         if (timeField.length != coarseTimeLength + fineTimeLength) {
435             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
436                                       timeField.length, coarseTimeLength + fineTimeLength);
437         }
438 
439         double seconds = 0;
440         for (int i = 0; i < coarseTimeLength; ++i) {
441             seconds = seconds * 256 + toUnsigned(timeField[i]);
442         }
443         double subseconds = 0;
444         for (int i = timeField.length - 1; i >= coarseTimeLength; --i) {
445             subseconds = (subseconds + toUnsigned(timeField[i])) / 256;
446         }
447 
448         return new AbsoluteDate(epoch, seconds).shiftedBy(subseconds);
449 
450     }
451 
452     /** Build an instance from a CCSDS Day Segmented Time Code (CDS).
453      * <p>
454      * CCSDS Day Segmented Time Code is defined in the blue book:
455      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
456      * </p>
457      * @param preambleField field specifying the format, often not transmitted in
458      * data interfaces, as it is constant for a given data interface
459      * @param timeField byte array containing the time code
460      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
461      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
462      * may be null in this case)
463      * @return an instance corresponding to the specified date
464      * @throws OrekitException if preamble is inconsistent with Day Segmented Time Code,
465      * or if it is inconsistent with time field, or if agency epoch is needed but not provided,
466      * or it UTC time scale cannot be retrieved
467      */
468     public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField, final byte[] timeField,
469                                                               final DateComponents agencyDefinedEpoch)
470         throws OrekitException {
471 
472         // time code identification
473         if ((preambleField & 0xF0) != 0x40) {
474             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
475                                       formatByte(preambleField));
476         }
477 
478         // reference epoch
479         final DateComponents epoch;
480         if ((preambleField & 0x08) == 0x00) {
481             // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
482             epoch = DateComponents.CCSDS_EPOCH;
483         } else {
484             // the reference epoch is agency defined
485             if (agencyDefinedEpoch == null) {
486                 throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
487             }
488             epoch = agencyDefinedEpoch;
489         }
490 
491         // time field lengths
492         final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3;
493         final int subMillisecondLength = (preambleField & 0x03) << 1;
494         if (subMillisecondLength == 6) {
495             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
496                                       formatByte(preambleField));
497         }
498         if (timeField.length != daySegmentLength + 4 + subMillisecondLength) {
499             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
500                                       timeField.length, daySegmentLength + 4 + subMillisecondLength);
501         }
502 
503 
504         int i   = 0;
505         int day = 0;
506         while (i < daySegmentLength) {
507             day = day * 256 + toUnsigned(timeField[i++]);
508         }
509 
510         long milliInDay = 0l;
511         while (i < daySegmentLength + 4) {
512             milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]);
513         }
514         final int milli   = (int) (milliInDay % 1000l);
515         final int seconds = (int) ((milliInDay - milli) / 1000l);
516 
517         double subMilli = 0;
518         double divisor  = 1;
519         while (i < timeField.length) {
520             subMilli = subMilli * 256 + toUnsigned(timeField[i++]);
521             divisor *= 1000;
522         }
523 
524         final DateComponents date = new DateComponents(epoch, day);
525         final TimeComponents time = new TimeComponents(seconds);
526         return new AbsoluteDate(date, time, TimeScalesFactory.getUTC()).shiftedBy(milli * 1.0e-3 + subMilli / divisor);
527 
528     }
529 
530     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
531      * <p>
532      * CCSDS Calendar Segmented Time Code is defined in the blue book:
533      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
534      * </p>
535      * @param preambleField field specifying the format, often not transmitted in
536      * data interfaces, as it is constant for a given data interface
537      * @param timeField byte array containing the time code
538      * @return an instance corresponding to the specified date
539      * @throws OrekitException if preamble is inconsistent with Calendar Segmented Time Code,
540      * or if it is inconsistent with time field, or it UTC time scale cannot be retrieved
541      */
542     public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField)
543         throws OrekitException {
544 
545         // time code identification
546         if ((preambleField & 0xF0) != 0x50) {
547             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
548                                       formatByte(preambleField));
549         }
550 
551         // time field length
552         final int length = 7 + (preambleField & 0x07);
553         if (length == 14) {
554             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
555                                       formatByte(preambleField));
556         }
557         if (timeField.length != length) {
558             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
559                                       timeField.length, length);
560         }
561 
562         // date part in the first four bytes
563         final DateComponents date;
564         if ((preambleField & 0x08) == 0x00) {
565             // month of year and day of month variation
566             date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
567                                       toUnsigned(timeField[2]),
568                                       toUnsigned(timeField[3]));
569         } else {
570             // day of year variation
571             date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
572                                       toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3]));
573         }
574 
575         // time part from bytes 5 to last (between 7 and 13 depending on precision)
576         final TimeComponents time = new TimeComponents(toUnsigned(timeField[4]),
577                                                        toUnsigned(timeField[5]),
578                                                        toUnsigned(timeField[6]));
579         double subSecond = 0;
580         double divisor   = 1;
581         for (int i = 7; i < length; ++i) {
582             subSecond = subSecond * 100 + toUnsigned(timeField[i]);
583             divisor *= 100;
584         }
585 
586         return new AbsoluteDate(date, time, TimeScalesFactory.getUTC()).shiftedBy(subSecond / divisor);
587 
588     }
589 
590     /** Decode a signed byte as an unsigned int value.
591      * @param b byte to decode
592      * @return an unsigned int value
593      */
594     private static int toUnsigned(final byte b) {
595         final int i = (int) b;
596         return (i < 0) ? 256 + i : i;
597     }
598 
599     /** Format a byte as an hex string for error messages.
600      * @param data byte to format
601      * @return a formatted string
602      */
603     private static String formatByte(final byte data) {
604         return "0x" + Integer.toHexString(data).toUpperCase();
605     }
606 
607     /** Build an instance corresponding to a GPS date.
608      * <p>GPS dates are provided as a week number starting at
609      * {@link #GPS_EPOCH GPS epoch} and as a number of milliseconds
610      * since week start.</p>
611      * @param weekNumber week number since {@link #GPS_EPOCH GPS epoch}
612      * @param milliInWeek number of milliseconds since week start
613      * @return a new instant
614      */
615     public static AbsoluteDate createGPSDate(final int weekNumber,
616                                              final double milliInWeek) {
617         final int day = (int) FastMath.floor(milliInWeek / (1000.0 * Constants.JULIAN_DAY));
618         final double secondsInDay = milliInWeek / 1000.0 - day * Constants.JULIAN_DAY;
619         return new AbsoluteDate(new DateComponents(DateComponents.GPS_EPOCH, weekNumber * 7 + day),
620                                 new TimeComponents(secondsInDay),
621                                 TimeScalesFactory.getGPS());
622     }
623 
624     /** Build an instance corresponding to a Julian Epoch (JE).
625      * <p>According to Lieske paper: <a
626      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&defaultprint=YES&filetype=.pdf.">
627      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
628      * vol. 73, no. 3, Mar. 1979, p. 282-284, Julian Epoch is related to Julian Ephemeris Date as:</p>
629      * <pre>
630      * JE = 2000.0 + (JED - 2451545.0) / 365.25
631      * </pre>
632      * <p>
633      * This method reverts the formula above and computes an {@code AbsoluteDate} from the Julian Epoch.
634      * </p>
635      * @param julianEpoch Julian epoch, like 2000.0 for defining the classical reference J2000.0
636      * @return a new instant
637      * @see #J2000_EPOCH
638      * @see #createBesselianEpoch(double)
639      */
640     public static AbsoluteDate createJulianEpoch(final double julianEpoch) {
641         return new AbsoluteDate(J2000_EPOCH,
642                                 Constants.JULIAN_YEAR * (julianEpoch - 2000.0));
643     }
644 
645     /** Build an instance corresponding to a Besselian Epoch (BE).
646      * <p>According to Lieske paper: <a
647      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&defaultprint=YES&filetype=.pdf.">
648      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
649      * vol. 73, no. 3, Mar. 1979, p. 282-284, Besselian Epoch is related to Julian Ephemeris Date as:</p>
650      * <pre>
651      * BE = 1900.0 + (JED - 2415020.31352) / 365.242198781
652      * </pre>
653      * <p>
654      * This method reverts the formula above and computes an {@code AbsoluteDate} from the Besselian Epoch.
655      * </p>
656      * @param besselianEpoch Besselian epoch, like 1950 for defining the classical reference B1950.0
657      * @return a new instant
658      * @see #createJulianEpoch(double)
659      */
660     public static AbsoluteDate createBesselianEpoch(final double besselianEpoch) {
661         return new AbsoluteDate(J2000_EPOCH,
662                                 MathArrays.linearCombination(Constants.BESSELIAN_YEAR, besselianEpoch - 1900,
663                                                              Constants.JULIAN_DAY, -36525,
664                                                              Constants.JULIAN_DAY, 0.31352));
665     }
666 
667     /** Get a time-shifted date.
668      * <p>
669      * Calling this method is equivalent to call <code>new AbsoluteDate(this, dt)</code>.
670      * </p>
671      * @param dt time shift in seconds
672      * @return a new date, shifted with respect to instance (which is immutable)
673      * @see org.orekit.utils.PVCoordinates#shiftedBy(double)
674      * @see org.orekit.attitudes.Attitude#shiftedBy(double)
675      * @see org.orekit.orbits.Orbit#shiftedBy(double)
676      * @see org.orekit.propagation.SpacecraftState#shiftedBy(double)
677      */
678     public AbsoluteDate shiftedBy(final double dt) {
679         return new AbsoluteDate(this, dt);
680     }
681 
682     /** Compute the physically elapsed duration between two instants.
683      * <p>The returned duration is the number of seconds physically
684      * elapsed between the two instants, measured in a regular time
685      * scale with respect to surface of the Earth (i.e either the {@link
686      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
687      * GPSScale GPS scale}). It is the only method that gives a
688      * duration with a physical meaning.</p>
689      * <p>This method gives the same result (with less computation)
690      * as calling {@link #offsetFrom(AbsoluteDate, TimeScale)}
691      * with a second argument set to one of the regular scales cited
692      * above.</p>
693      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
694      * double)} constructor.</p>
695      * @param instant instant to subtract from the instance
696      * @return offset in seconds between the two instants (positive
697      * if the instance is posterior to the argument)
698      * @see #offsetFrom(AbsoluteDate, TimeScale)
699      * @see #AbsoluteDate(AbsoluteDate, double)
700      */
701     public double durationFrom(final AbsoluteDate instant) {
702         return (epoch - instant.epoch) + (offset - instant.offset);
703     }
704 
705     /** Compute the apparent clock offset between two instant <em>in the
706      * perspective of a specific {@link TimeScale time scale}</em>.
707      * <p>The offset is the number of seconds counted in the given
708      * time scale between the locations of the two instants, with
709      * all time scale irregularities removed (i.e. considering all
710      * days are exactly 86400 seconds long). This method will give
711      * a result that may not have a physical meaning if the time scale
712      * is irregular. For example since a leap second was introduced at
713      * the end of 2005, the apparent offset between 2005-12-31T23:59:59
714      * and 2006-01-01T00:00:00 is 1 second, but the physical duration
715      * of the corresponding time interval as returned by the {@link
716      * #durationFrom(AbsoluteDate)} method is 2 seconds.</p>
717      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
718      * double, TimeScale)} constructor.</p>
719      * @param instant instant to subtract from the instance
720      * @param timeScale time scale with respect to which the offset should
721      * be computed
722      * @return apparent clock offset in seconds between the two instants
723      * (positive if the instance is posterior to the argument)
724      * @see #durationFrom(AbsoluteDate)
725      * @see #AbsoluteDate(AbsoluteDate, double, TimeScale)
726      */
727     public double offsetFrom(final AbsoluteDate instant, final TimeScale timeScale) {
728         final long   elapsedDurationA = epoch - instant.epoch;
729         final double elapsedDurationB = (offset         + timeScale.offsetFromTAI(this)) -
730                                         (instant.offset + timeScale.offsetFromTAI(instant));
731         return  elapsedDurationA + elapsedDurationB;
732     }
733 
734     /** Compute the offset between two time scales at the current instant.
735      * <p>The offset is defined as <i>l₁-l₂</i>
736      * where <i>l₁</i> is the location of the instant in
737      * the <code>scale1</code> time scale and <i>l₂</i> is the
738      * location of the instant in the <code>scale2</code> time scale.</p>
739      * @param scale1 first time scale
740      * @param scale2 second time scale
741      * @return offset in seconds between the two time scales at the
742      * current instant
743      */
744     public double timeScalesOffset(final TimeScale scale1, final TimeScale scale2) {
745         return scale1.offsetFromTAI(this) - scale2.offsetFromTAI(this);
746     }
747 
748     /** Convert the instance to a Java {@link java.util.Date Date}.
749      * <p>Conversion to the Date class induces a loss of precision because
750      * the Date class does not provide sub-millisecond information. Java Dates
751      * are considered to be locations in some times scales.</p>
752      * @param timeScale time scale to use
753      * @return a {@link java.util.Date Date} instance representing the location
754      * of the instant in the time scale
755      */
756     public Date toDate(final TimeScale timeScale) {
757         final double time = epoch + (offset + timeScale.offsetFromTAI(this));
758         return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000));
759     }
760 
761     /** Split the instance into date/time components.
762      * @param timeScale time scale to use
763      * @return date/time components
764      */
765     public DateTimeComponents getComponents(final TimeScale timeScale) {
766 
767         // compute offset from 2000-01-01T00:00:00 in specified time scale exactly,
768         // using Møller-Knuth TwoSum algorithm without branching
769         // the following statements must NOT be simplified, they rely on floating point
770         // arithmetic properties (rounding and representable numbers)
771         // at the end, the EXACT result of addition offset + timeScale.offsetFromTAI(this)
772         // is sum + residual, where sum is the closest representable number to the exact
773         // result and residual is the missing part that does not fit in the first number
774         final double taiOffset = timeScale.offsetFromTAI(this);
775         final double sum       = offset + taiOffset;
776         final double oPrime    = sum - taiOffset;
777         final double dPrime    = sum - oPrime;
778         final double deltaO    = offset - oPrime;
779         final double deltaD    = taiOffset - dPrime;
780         final double residual  = deltaO + deltaD;
781 
782         // split date and time
783         final long   carry = (long) FastMath.floor(sum);
784         double offset2000B = (sum - carry) + residual;
785         long   offset2000A = epoch + carry + 43200l;
786         if (offset2000B < 0) {
787             offset2000A -= 1;
788             offset2000B += 1;
789         }
790         long time = offset2000A % 86400l;
791         if (time < 0l) {
792             time += 86400l;
793         }
794         final int date = (int) ((offset2000A - time) / 86400l);
795 
796         // extract calendar elements
797         final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date);
798         TimeComponents timeComponents = new TimeComponents((int) time, offset2000B);
799 
800         if (timeScale instanceof UTCScale) {
801             final UTCScale utc = (UTCScale) timeScale;
802             if (utc.insideLeap(this)) {
803                 // fix the seconds number to take the leap into account
804                 timeComponents = new TimeComponents(timeComponents.getHour(), timeComponents.getMinute(),
805                                                     timeComponents.getSecond() + utc.getLeap(this));
806             }
807         }
808 
809         // build the components
810         return new DateTimeComponents(dateComponents, timeComponents);
811 
812     }
813 
814     /** Compare the instance with another date.
815      * @param date other date to compare the instance to
816      * @return a negative integer, zero, or a positive integer as this date
817      * is before, simultaneous, or after the specified date.
818      */
819     public int compareTo(final AbsoluteDate date) {
820         final double delta = durationFrom(date);
821         if (delta < 0) {
822             return -1;
823         } else if (delta > 0) {
824             return +1;
825         }
826         return 0;
827     }
828 
829     /** {@inheritDoc} */
830     public AbsoluteDate getDate() {
831         return this;
832     }
833 
834     /** Check if the instance represent the same time as another instance.
835      * @param date other date
836      * @return true if the instance and the other date refer to the same instant
837      */
838     public boolean equals(final Object date) {
839 
840         if (date == this) {
841             // first fast check
842             return true;
843         }
844 
845         if ((date != null) && (date instanceof AbsoluteDate)) {
846             return durationFrom((AbsoluteDate) date) == 0;
847         }
848 
849         return false;
850 
851     }
852 
853     /** Get a hashcode for this date.
854      * @return hashcode
855      */
856     public int hashCode() {
857         final long l = Double.doubleToLongBits(durationFrom(J2000_EPOCH));
858         return (int) (l ^ (l >>> 32));
859     }
860 
861     /** Get a String representation of the instant location in UTC time scale.
862      * @return a string representation of the instance,
863      * in ISO-8601 format with milliseconds accuracy
864      */
865     public String toString() {
866         try {
867             return toString(TimeScalesFactory.getUTC());
868         } catch (OrekitException oe) {
869             throw new RuntimeException(oe);
870         }
871     }
872 
873     /** Get a String representation of the instant location.
874      * @param timeScale time scale to use
875      * @return a string representation of the instance,
876      * in ISO-8601 format with milliseconds accuracy
877      */
878     public String toString(final TimeScale timeScale) {
879         return getComponents(timeScale).toString();
880     }
881 
882 }