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  
19  import org.junit.jupiter.api.AfterEach;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  import org.orekit.Utils;
24  import org.orekit.frames.EOPHistory;
25  import org.orekit.frames.ITRFVersion;
26  import org.orekit.utils.Constants;
27  import org.orekit.utils.IERSConventions;
28  
29  import java.util.ArrayList;
30  import java.util.List;
31  import java.util.concurrent.Callable;
32  import java.util.concurrent.ExecutionException;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.Future;
35  
36  public class UT1ScaleTest {
37  
38      @Test
39      public void testLeap2006() {
40          TimeScale utc = TimeScalesFactory.getUTC();
41          AbsoluteDate dateA = new AbsoluteDate(2005, 12, 30, 23, 59,  0.0, utc);
42          AbsoluteDate dateB = new AbsoluteDate(2006,  1,  2,  0,  1,  0.0, utc);
43          TimeOffset deltaAUT1 = ut1.offsetFromTAI(dateA);
44          TimeOffset deltaAUTC = utc.offsetFromTAI(dateA);
45          TimeOffset deltaBUT1 = ut1.offsetFromTAI(dateB);
46          TimeOffset deltaBUTC = utc.offsetFromTAI(dateB);
47  
48          // there is a leap second between the two dates
49          Assertions.assertEquals(deltaAUTC.toDouble() - 1.0, deltaBUTC.toDouble(), 1.0e-15);
50  
51          // the leap second induces UTC goes from above UT1 to below UT1
52          Assertions.assertTrue(deltaAUTC.compareTo(deltaAUT1) > 0);
53          Assertions.assertTrue(deltaBUTC.compareTo(deltaBUT1) < 0);
54  
55          // UT1 is continuous, so change should be very small in two days
56          Assertions.assertEquals(deltaAUT1.toDouble(), deltaBUT1.toDouble(), 3.0e-4);
57  
58      }
59  
60      /** Issue #636 */
61      @Test
62      public void testContinuousDuringLeap() {
63          // setup
64          TimeScale utc = TimeScalesFactory.getUTC();
65          AbsoluteDate dateA = new AbsoluteDate(2005, 12, 31, 23, 59, 0.0, utc);
66          AbsoluteDate dateB = new AbsoluteDate(2006, 1, 1, 0, 1, 0.0, utc);
67          EOPHistory eopHistory = ut1.getEOPHistory();
68  
69          // verify
70          // there is a leap second and jump in UT1-UTC
71          Assertions.assertEquals(eopHistory.getUT1MinusUTC(new AbsoluteDate(2005, 12, 31, utc)), -0.6611333, 0);
72          Assertions.assertEquals(eopHistory.getUT1MinusUTC(new AbsoluteDate(2006, 1, 1, utc)), 0.338829, 1e-16);
73  
74          // check UT1-TAI is still smooth
75          double dt = 0.5;
76          double tol = 0.001 * dt / Constants.JULIAN_DAY;
77          double previous = ut1.offsetFromTAI(dateA.shiftedBy(-dt)).toDouble();
78          for (AbsoluteDate d = dateA; d.compareTo(dateB) < 0; d = d.shiftedBy(dt)) {
79              double actual = ut1.offsetFromTAI(d).toDouble();
80              Assertions.assertEquals(0, actual - previous, tol, "at " + d.toString(utc));
81              previous = actual;
82          }
83      }
84  
85      @Test
86      public void testSymmetry() {
87          for (double dt = -10000; dt < 10000; dt += 123.456789) {
88              AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt * Constants.JULIAN_DAY);
89              double dt1 = ut1.offsetFromTAI(date).toDouble();
90              DateTimeComponents components = date.getComponents(ut1);
91              double dt2 = ut1.offsetToTAI(components.getDate(), components.getTime()).toDouble();
92              Assertions.assertEquals( 0.0, dt1 + dt2, 1.0e-10);
93          }
94      }
95  
96      @Test
97      public void testConcurrent() throws InterruptedException, ExecutionException {
98          // set up
99          final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
100         final int threads = 10;
101         final int timesPerThread = 100;
102         final double dt = 123.456789 * Constants.JULIAN_DAY;
103 
104         // calculate expected values in single-thread mode
105         final double[] expected = new double[timesPerThread];
106         for (int i = 0; i < timesPerThread; i++) {
107             expected[i] = ut1.offsetFromTAI(date.shiftedBy(i * dt)).toDouble();
108         }
109 
110         // build jobs for concurrent execution
111         final List<Callable<Boolean>> jobs = new ArrayList<>();
112         for (int i = 0; i < threads; i++) {
113             jobs.add(() -> {
114                 for (int j = 0; j < timesPerThread; j++) {
115                     final double actual = ut1.offsetFromTAI(date.shiftedBy(j * dt)).toDouble();
116                     Assertions.assertEquals(expected[j], actual, 0);
117                 }
118                 return true;
119             });
120         }
121 
122         // action
123         final List<Future<Boolean>> futures = Executors.newFixedThreadPool(threads).invokeAll(jobs);
124 
125         // verify - necessary to throw AssertionErrors from the Callable
126         for (Future<Boolean> future : futures) {
127             Assertions.assertEquals(true, future.get());
128         }
129     }
130 
131     @Test
132     public void testAAS06134() {
133 
134         // this reference test has been extracted from the following paper:
135         // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
136         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
137         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
138         // Note that the dUT1 here is -0.439962, whereas it is -0.4399619 in the book
139         Utils.setLoaders(IERSConventions.IERS_1996,
140                          Utils.buildEOPList(IERSConventions.IERS_1996, ITRFVersion.ITRF_2008, new double[][] {
141                              { 53098, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
142                              { 53099, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
143                              { 53100, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
144                              { 53101, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
145                              { 53102, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
146                              { 53103, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
147                              { 53104, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
148                              { 53105, -0.439962, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN }
149                          }));
150         AbsoluteDate date =
151                 new AbsoluteDate(2004, 4, 6, 7, 51, 28.386009, TimeScalesFactory.getUTC());
152         DateTimeComponents components = date.getComponents(TimeScalesFactory.getUT1(IERSConventions.IERS_1996, true));
153         Assertions.assertEquals(2004,        components.getDate().getYear());
154         Assertions.assertEquals(   4,        components.getDate().getMonth());
155         Assertions.assertEquals(   6,        components.getDate().getDay());
156         Assertions.assertEquals(   7,        components.getTime().getHour());
157         Assertions.assertEquals(  51,        components.getTime().getMinute());
158         Assertions.assertEquals(  27.946047, components.getTime().getSecond(), 1.0e-10);
159     }
160 
161     @Test
162     public void testDuringLeap() {
163         final TimeScale utc   = TimeScalesFactory.getUTC();
164         final TimeScale scale = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
165         final AbsoluteDate before = new AbsoluteDate(new DateComponents(1983, 6, 30),
166                                                      new TimeComponents(23, 59, 59),
167                                                      utc);
168         final AbsoluteDate during = before.shiftedBy(1.25);
169         Assertions.assertEquals(61, utc.minuteDuration(during));
170         Assertions.assertEquals(1.0, utc.getLeap(during).toDouble(), 1.0e-10);
171         Assertions.assertEquals(60, scale.minuteDuration(during));
172         Assertions.assertEquals(0.0, scale.getLeap(during).toDouble(), 1.0e-10);
173     }
174 
175     @BeforeEach
176     public void setUp() {
177         Utils.setDataRoot("regular-data");
178         ut1 = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
179     }
180 
181     @AfterEach
182     public void tearDown() {
183         ut1 = null;
184     }
185 
186     private UT1Scale ut1;
187 
188 }