1   /* Copyright 2002-2024 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 java.io.Serializable;
20  
21  import org.hipparchus.util.FastMath;
22  import org.orekit.annotation.DefaultDataContext;
23  import org.orekit.data.DataContext;
24  import org.orekit.propagation.analytical.gnss.data.GNSSConstants;
25  import org.orekit.utils.Constants;
26  
27  /**
28   * Container for date in GLONASS form.
29   * @author Bryan Cazabonne
30   * @see AbsoluteDate
31   * @see "GLONASS Interface Control Document v1.0, 2016"
32   * @since 10.0
33   */
34  public class GLONASSDate implements Serializable, TimeStamped {
35  
36      /** Serializable UID. */
37      private static final long serialVersionUID = 20190131L;
38  
39      /** Constant for date computation. */
40      private static final int C1 = 44195;
41  
42      /** Constant for date computation. */
43      private static final int C2 = 45290;
44  
45      /** The number of the current day in a four year interval N<sub>a</sub>. */
46      private final int na;
47  
48      /** The number of the current four year interval N<sub>4</sub>. */
49      private final int n4;
50  
51      /** Number of seconds since N<sub>a</sub>. */
52      private final double secInNa;
53  
54      /** Current Julian date JD0. */
55      private double jd0;
56  
57      /** Greenwich Mean Sidereal Time (rad). */
58      private double gmst;
59  
60      /** Corresponding date. */
61      private final transient AbsoluteDate date;
62  
63      /** Build an instance corresponding to a GLONASS date.
64       *
65       * <p>This method uses the {@link DataContext#getDefault() default data context}.
66       *
67       * @param na the number of the current day in a four year interval
68       * @param n4 the number of the current four year interval
69       * @param secInNa the number of seconds since na start
70       * @see #GLONASSDate(int, int, double, TimeScale)
71       */
72      @DefaultDataContext
73      public GLONASSDate(final int na, final int n4, final double secInNa) {
74          this(na, n4, secInNa, DataContext.getDefault().getTimeScales().getGLONASS());
75      }
76  
77      /**
78       * Build an instance corresponding to a GLONASS date.
79       *
80       * @param na      the number of the current day in a four year interval
81       * @param n4      the number of the current four year interval
82       * @param secInNa the number of seconds since na start
83       * @param glonass time scale.
84       * @since 10.1
85       */
86      public GLONASSDate(final int na,
87                         final int n4,
88                         final double secInNa,
89                         final TimeScale glonass) {
90          this.na      = na;
91          this.n4      = n4;
92          this.secInNa = secInNa;
93          // Compute JD0
94          final int ratio = FastMath.round((float) (na - 3) / (25 + C1 + C2));
95          this.jd0  = 1461 * (n4 - 1) + na + 2450082.5 - ratio;
96          // GMST
97          this.gmst = computeGMST();
98          this.date = computeDate(glonass);
99      }
100 
101     /** Build an instance from an absolute date.
102      *
103      * <p>This method uses the {@link DataContext#getDefault() default data context}.
104      *
105      * @param date absolute date to consider
106      * @see #GLONASSDate(AbsoluteDate, TimeScale)
107      */
108     @DefaultDataContext
109     public GLONASSDate(final AbsoluteDate date) {
110         this(date, DataContext.getDefault().getTimeScales().getGLONASS());
111     }
112 
113     /**
114      * Build an instance from an absolute date.
115      *
116      * @param date    absolute date to consider
117      * @param glonass time scale.
118      * @since 10.1
119      */
120     public GLONASSDate(final AbsoluteDate date, final TimeScale glonass) {
121         final DateTimeComponents dateTime = date.getComponents(glonass);
122         // N4
123         final int year = dateTime.getDate().getYear();
124         this.n4 = ((int) (year - 1996) / 4) + 1;
125         // Na
126         final int start = 1996 + 4 * (n4 - 1);
127         final double duration = date.durationFrom(new AbsoluteDate(start, 1, 1, glonass));
128         this.na = (int) (duration / 86400) + 1;
129         this.secInNa = dateTime.getTime().getSecondsInLocalDay();
130         // Compute JD0
131         final int ratio = FastMath.round((float) (na - 3) / (25 + C1 + C2));
132         this.jd0 = 1461 * (n4 - 1) + na + 2450082.5 - ratio;
133         // GMST
134         this.gmst = computeGMST();
135         this.date = date;
136     }
137 
138     @Override
139     public AbsoluteDate getDate() {
140         return date;
141     }
142 
143     /** Get the number of seconds since N<sub>a</sub> start.
144      * @return number of seconds since N<sub>a</sub> start
145      */
146     public double getSecInDay() {
147         return secInNa;
148     }
149 
150     /** Get the number of the current day in a four year interval.
151      * @return the number of the current day in a four year interval
152      */
153     public int getDayNumber() {
154         return na;
155     }
156 
157     /** Get the number of the current four year interval.
158      * @return the number of the current four year interval
159      */
160     public int getIntervalNumber() {
161         return n4;
162     }
163 
164     /** Get the current Julian date JD0.
165      * @return the current date JD0
166      */
167     public double getJD0() {
168         return jd0;
169     }
170 
171     /** Get the Greenwich Mean Sidereal Time.
172      * @return the Greenwich Mean Sidereal Time (rad)
173      */
174     public double getGMST() {
175         return gmst;
176     }
177 
178     /** Compute the Greenwich Mean Sidereal Time using the current Julian date JD0.
179      * @return the Greenwich Mean Sidereal Time (rad)
180      */
181     private double computeGMST() {
182         final double ref = 2451545.0;
183         // Earth's rotation angle in radians
184         final double era = 2. * GNSSConstants.GLONASS_PI *
185                         (0.7790572732640 + 1.00273781191135448 * (jd0 - ref));
186         // Time from Epoch 2000 (1st January, 00:00 UTC) till current Epoch in Julian centuries
187         final double time = (jd0 - ref) / Constants.JULIAN_CENTURY;
188         // Time to the power n
189         final double time2 = time * time;
190         final double time3 = time2 * time;
191         final double time4 = time2 * time2;
192         final double time5 = time2 * time3;
193         // GMST computation
194         final double gTime = era + 7.03270726e-8 + time * 2.23603658710194e-2 +
195                         time2 * 6.7465784654e-6 - time3 * 2.1332e-12 - time4 * 1.452308e-10 - time5 * 1.784e-13;
196         return gTime;
197     }
198 
199     /** Compute the GLONASS date.
200      * @return the date
201      * @param glonass time scale.
202      */
203     private AbsoluteDate computeDate(final TimeScale glonass) {
204         // Compute the number of Julian day for the current date
205         final double jdn = jd0 + 0.5;
206         // Coefficients
207         final int a = (int) (jdn + 32044);
208         final int b = (4 * a + 3) / 146097;
209         final int c = a - (146097 * b) / 4;
210         final int d = (4 * c + 3) / 1461;
211         final int e = c - (1461 * d) / 4;
212         final int m = (5 * e + 2) / 153;
213         // Year, month and day
214         final int day   = e - (153 * m + 2) / 5 + 1;
215         final int month = m + 3 - 12 * (m / 10);
216         final int year  = 100 * b + d - 4800 + m / 10;
217         return new AbsoluteDate(new DateComponents(year, month, day),
218                                 new TimeComponents(secInNa),
219                                 glonass);
220     }
221 
222     /** Replace the instance with a data transfer object for serialization.
223      * @return data transfer object that will be serialized
224      */
225     @DefaultDataContext
226     private Object writeReplace() {
227         return new DataTransferObject(na, n4, secInNa);
228     }
229 
230     /** Internal class used only for serialization. */
231     @DefaultDataContext
232     private static class DataTransferObject implements Serializable {
233 
234         /** Serializable UID. */
235         private static final long serialVersionUID = 20190131L;
236 
237         /** The number of the current day in a four year interval N<sub>a</sub>. */
238         private final int na;
239 
240         /** The number of the current four year interval N<sub>4</sub>. */
241         private final int n4;
242 
243         /** Number of seconds since N<sub>a</sub>. */
244         private final double secInNa;
245 
246         /** Simple constructor.
247          * @param na the number of the current day in a four year interval
248          * @param n4 the number of the current four year interval
249          * @param secInNa the number of seconds since na start
250          */
251         DataTransferObject(final int na, final int n4, final double secInNa) {
252             this.na      = na;
253             this.n4      = n4;
254             this.secInNa = secInNa;
255         }
256 
257         /** Replace the deserialized data transfer object with a {@link GPSDate}.
258          * @return replacement {@link GPSDate}
259          */
260         private Object readResolve() {
261             return new GLONASSDate(na, n4, secInNa);
262         }
263 
264     }
265 
266 }