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