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 }