UTCTAIOffset.java

  1. /* Copyright 2002-2025 CS GROUP
  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. import java.io.Serializable;

  19. import org.hipparchus.CalculusFieldElement;

  20. /** Offset between {@link UTCScale UTC} and  {@link TAIScale TAI} time scales.
  21.  * <p>The {@link UTCScale UTC} and  {@link TAIScale TAI} time scales are two
  22.  * scales offset with respect to each other. The {@link TAIScale TAI} scale is
  23.  * continuous whereas the {@link UTCScale UTC} includes some discontinuity when
  24.  * leap seconds are introduced by the <a href="http://www.iers.org/">International
  25.  * Earth Rotation Service</a> (IERS).</p>
  26.  * <p>This class represents the offset between the two scales that is
  27.  * valid between two leap seconds occurrences. It handles both the linear offsets
  28.  * used from 1961-01-01 to 1971-12-31 and the constant integer offsets used since
  29.  * 1972-01-01.</p>
  30.  * @author Luc Maisonobe
  31.  * @see UTCScale
  32.  * @see UTCTAIHistoryFilesLoader
  33.  */
  34. public class UTCTAIOffset implements TimeStamped, Serializable {

  35.     /** Serializable UID. */
  36.     private static final long serialVersionUID = 20240720L;

  37.     /** Nanoseconds in one second. */
  38.     private static final int NANOS_IN_SECOND = 1000000000;

  39.     /** Leap date. */
  40.     private final AbsoluteDate leapDate;

  41.     /** Leap date in Modified Julian Day. */
  42.     private final int leapDateMJD;

  43.     /** Offset start of validity date. */
  44.     private final AbsoluteDate validityStart;

  45.     /** Reference date for the slope multiplication as Modified Julian Day. */
  46.     private final int mjdRef;

  47.     /** Reference date for the slope multiplication. */
  48.     private final AbsoluteDate reference;

  49.     /** Value of the leap at offset validity start (in seconds). */
  50.     private final TimeOffset leap;

  51.     /** Offset at validity start in seconds (TAI minus UTC). */
  52.     private final TimeOffset offset;

  53.     /** Offset slope in nanoseconds per UTC second (TAI minus UTC / dUTC). */
  54.     private final int slope;

  55.     /** Simple constructor for a linear model.
  56.      * @param leapDate leap date
  57.      * @param leapDateMJD leap date in Modified Julian Day
  58.      * @param leap value of the leap at offset validity start (in seconds)
  59.      * @param offset offset in seconds (TAI minus UTC)
  60.      * @param mjdRef reference date for the slope multiplication as Modified Julian Day
  61.      * @param slope offset slope in nanoseconds per UTC second (TAI minus UTC / dUTC)
  62.      * @param reference date for slope computations.
  63.      */
  64.     UTCTAIOffset(final AbsoluteDate leapDate, final int leapDateMJD,
  65.                  final TimeOffset leap, final TimeOffset offset,
  66.                  final int mjdRef, final int slope, final AbsoluteDate reference) {
  67.         this.leapDate      = leapDate;
  68.         this.leapDateMJD   = leapDateMJD;
  69.         this.validityStart = leapDate.shiftedBy(leap);
  70.         this.mjdRef        = mjdRef;
  71.         this.reference     = reference;
  72.         this.leap          = leap;
  73.         this.offset        = offset;

  74.         // at some absolute instant t₀, we can associate reading a₀ on a TAI clock and u₀ on a UTC clock
  75.         // at this instant, the offset between TAI and UTC is therefore τ₀ = a₀ - u₀
  76.         // at another absolute instant t₁, we can associate reading a₁ on a TAI clock and u₁ on a UTC clock
  77.         // at this instant, the offset between TAI and UTC is therefore τ₁ = a₁ - u₁
  78.         // the slope is defined according to offsets counted in UTC, i.e.:
  79.         // τ₁ = τ₀ + (u₁ - u₀) * slope/n (where n = 10⁹ because the slope is in ns/s)
  80.         // if we have a₁ - a₀ (i.e. dates in TAI) instead of u₁ - u₀, we need to invert the expression
  81.         // we get: τ₁ = τ₀ + (a₁ - a₀) * slope / (n + slope)
  82.         this.slope = slope;

  83.     }

  84.     /** Get the date of the start of the leap.
  85.      * @return date of the start of the leap
  86.      * @see #getValidityStart()
  87.      */
  88.     public AbsoluteDate getDate() {
  89.         return leapDate;
  90.     }

  91.     /** Get the date of the start of the leap as Modified Julian Day.
  92.      * @return date of the start of the leap as Modified Julian Day
  93.      */
  94.     public int getMJD() {
  95.         return leapDateMJD;
  96.     }

  97.     /** Get the start time of validity for this offset.
  98.      * <p>The start of the validity of the offset is {@link #getLeap()}
  99.      * seconds after the start of the leap itself.</p>
  100.      * @return start of validity date
  101.      * @see #getDate()
  102.      */
  103.     public AbsoluteDate getValidityStart() {
  104.         return validityStart;
  105.     }

  106.     /** Get the value of the leap at offset validity start.
  107.      * @return value of the leap at offset validity start
  108.      */
  109.     public TimeOffset getLeap() {
  110.         return leap;
  111.     }

  112.     /** Get the TAI - UTC offset in seconds.
  113.      * @param date date at which the offset is requested
  114.      * @return TAI - UTC offset in seconds.
  115.      */
  116.     public TimeOffset getOffset(final AbsoluteDate date) {
  117.         if (slope == 0) {
  118.             // we use an if statement here so the offset computation returns
  119.             // a finite value when date is AbsoluteDate.FUTURE_INFINITY
  120.             // without this if statement, the multiplication between an
  121.             // infinite duration and a zero slope would induce a NaN offset
  122.             return offset;
  123.         } else {

  124.             // time during which slope applies
  125.             final TimeOffset delta = date.accurateDurationFrom(reference);

  126.             // accumulated drift
  127.             final TimeOffset drift = delta.multiply(slope).divide(slope + NANOS_IN_SECOND);

  128.             return offset.add(drift);

  129.         }
  130.     }

  131.     /** Get the TAI - UTC offset in seconds.
  132.      * @param date date at which the offset is requested
  133.      * @param <T> type of the filed elements
  134.      * @return TAI - UTC offset in seconds.
  135.      * @since 9.0
  136.      */
  137.     public <T extends CalculusFieldElement<T>> T getOffset(final FieldAbsoluteDate<T> date) {
  138.         if (slope == 0) {
  139.             // we use an if statement here so the offset computation returns
  140.             // a finite value when date is FieldAbsoluteDate.getFutureInfinity(field)
  141.             // without this if statement, the multiplication between an
  142.             // infinite duration and a zero slope would induce a NaN offset
  143.             return date.getField().getZero().newInstance(offset.toDouble());
  144.         } else {
  145.             // TODO perform complete computation
  146.             return date.getField().getZero().newInstance(getOffset(date.toAbsoluteDate()).toDouble());
  147.         }
  148.     }

  149.     /** Get the TAI - UTC offset in seconds.
  150.      * @param date date components (in UTC) at which the offset is requested
  151.      * @param time time components (in UTC) at which the offset is requested
  152.      * @return TAI - UTC offset in seconds.
  153.      */
  154.     public TimeOffset getOffset(final DateComponents date, final TimeComponents time) {
  155.         if (slope == 0) {
  156.             return offset;
  157.         } else {

  158.             // time during which slope applies
  159.             final TimeOffset delta = new TimeOffset((date.getMJD() - mjdRef) * TimeOffset.DAY.getSeconds() +
  160.                                                   time.getHour() * TimeOffset.HOUR.getSeconds() +
  161.                                                   time.getMinute() * TimeOffset.MINUTE.getSeconds() +
  162.                                                   time.getSplitSecond().getSeconds(),
  163.                                                     time.getSplitSecond().getAttoSeconds());

  164.             // accumulated drift
  165.             final TimeOffset drift = delta.multiply(slope).divide(NANOS_IN_SECOND);

  166.             return offset.add(drift);

  167.         }
  168.     }

  169. }