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 import java.util.List;
21 import java.util.concurrent.atomic.AtomicReference;
22
23 import org.hipparchus.util.FastMath;
24 import org.orekit.frames.EOPEntry;
25 import org.orekit.utils.Constants;
26 import org.orekit.utils.IERSConventions;
27
28 /** Container for date in GPS form.
29 * @author Luc Maisonobe
30 * @see AbsoluteDate
31 * @since 9.3
32 */
33 public class GPSDate implements Serializable, TimeStamped {
34
35 /** Serializable UID. */
36 private static final long serialVersionUID = 20180633L;
37
38 /** Duration of a week in days. */
39 private static final int WEEK_D = 7;
40
41 /** Duration of a week in seconds. */
42 private static final double WEEK_S = WEEK_D * Constants.JULIAN_DAY;
43
44 /** Number of weeks in one rollover cycle. */
45 private static final int CYCLE_W = 1024;
46
47 /** Number of days in one rollover cycle. */
48 private static final int CYCLE_D = WEEK_D * CYCLE_W;
49
50 /** Conversion factor from seconds to milliseconds. */
51 private static final double S_TO_MS = 1000.0;
52
53 /** Reference date for ensuring continuity across GPS week rollover.
54 * @since 9.3.1
55 */
56 private static AtomicReference<DateComponents> rolloverReference = new AtomicReference<DateComponents>(null);
57
58 /** Week number since {@link AbsoluteDate#GPS_EPOCH GPS epoch}. */
59 private final int weekNumber;
60
61 /** Number of milliseconds since week start. */
62 private final double milliInWeek;
63
64 /** Corresponding date. */
65 private final transient AbsoluteDate date;
66
67 /** Build an instance corresponding to a GPS date.
68 * <p>
69 * GPS dates are provided as a week number starting at {@link AbsoluteDate#GPS_EPOCH GPS epoch}
70 * and as a number of milliseconds since week start.
71 * </p>
72 * <p>
73 * Many interfaces provide only the 10 lower bits of the GPS week number, just as it comes from
74 * the GPS signal. In other words they use a week number modulo 1024. In order to cope with
75 * this, when the week number is smaller than 1024, this constructor assumes a modulo operation
76 * has been performed and it will fix the week number according to the reference date set up for
77 * handling rollover (see {@link #setRolloverReference(DateComponents) setRolloverReference(reference)}).
78 * If the week number is 1024 or larger, it will be used without any correction.
79 * </p>
80 * @param weekNumber week number, either absolute or modulo 1024
81 * @param milliInWeek number of milliseconds since week start
82 */
83 public GPSDate(final int weekNumber, final double milliInWeek) {
84
85 final int day = (int) FastMath.floor(milliInWeek / (Constants.JULIAN_DAY * S_TO_MS));
86 final double secondsInDay = milliInWeek / S_TO_MS - day * Constants.JULIAN_DAY;
87
88 int w = weekNumber;
89 DateComponents#DateComponents">DateComponents dc = new DateComponents(DateComponents.GPS_EPOCH, weekNumber * 7 + day);
90 if (weekNumber < 1024) {
91
92 DateComponents reference = rolloverReference.get();
93 if (reference == null) {
94 // lazy setting of a default reference, using end of EOP entries
95 final UT1Scale ut1 = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
96 final List<EOPEntry> eop = ut1.getEOPHistory().getEntries();
97 final int lastMJD = eop.get(eop.size() - 1).getMjd();
98 reference = new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, lastMJD);
99 rolloverReference.compareAndSet(null, reference);
100 }
101
102 // fix GPS week rollover
103 while (dc.getJ2000Day() < reference.getJ2000Day() - CYCLE_D / 2) {
104 dc = new DateComponents(dc, CYCLE_D);
105 w += CYCLE_W;
106 }
107
108 }
109
110 this.weekNumber = w;
111 this.milliInWeek = milliInWeek;
112
113 date = new AbsoluteDate(dc, new TimeComponents(secondsInDay), TimeScalesFactory.getGPS());
114
115 }
116
117 /** Build an instance from an absolute date.
118 * @param date absolute date to consider
119 */
120 public GPSDate(final AbsoluteDate date) {
121
122 this.weekNumber = (int) FastMath.floor(date.durationFrom(AbsoluteDate.GPS_EPOCH) / WEEK_S);
123 final AbsoluteDateteDate">AbsoluteDate weekStart = new AbsoluteDate(AbsoluteDate.GPS_EPOCH, WEEK_S * weekNumber);
124 this.milliInWeek = date.durationFrom(weekStart) * S_TO_MS;
125 this.date = date;
126
127 }
128
129 /** Set a reference date for ensuring continuity across GPS week rollover.
130 * <p>
131 * Instance created using the {@link #GPSDate(int, double) GPSDate(weekNumber, milliInWeek)}
132 * constructor and with a week number between 0 and 1024 after this method has been called will
133 * fix the week number to ensure they correspond to dates between {@code reference - 512 weeks}
134 * and {@code reference + 512 weeks}.
135 * </p>
136 * <p>
137 * If this method is never called, a default reference date for rollover will be set using
138 * the date of the last known EOP entry retrieved from {@link UT1Scale#getEOPHistory() UT1}
139 * time scale.
140 * </p>
141 * @param reference reference date for GPS week rollover
142 * @see #getRolloverReference()
143 * @see #GPSDate(int, double)
144 * @since 9.3.1
145 */
146 public static void setRolloverReference(final DateComponents reference) {
147 rolloverReference.set(reference);
148 }
149
150 /** Get the reference date ensuring continuity across GPS week rollover.
151 * @return reference reference date for GPS week rollover
152 * @see #setRolloverReference(AbsoluteDate)
153 * @see #GPSDate(int, double)
154 * @since 9.3.1
155 */
156 public static DateComponents getRolloverReference() {
157 return rolloverReference.get();
158 }
159
160 /** Get the week number since {@link AbsoluteDate#GPS_EPOCH GPS epoch}.
161 * <p>
162 * The week number returned here has been fixed for GPS week rollover, i.e.
163 * it may be larger than 1024.
164 * </p>
165 * @return week number since {@link AbsoluteDate#GPS_EPOCH GPS epoch}
166 */
167 public int getWeekNumber() {
168 return weekNumber;
169 }
170
171 /** Get the number of milliseconds since week start.
172 * @return number of milliseconds since week start
173 */
174 public double getMilliInWeek() {
175 return milliInWeek;
176 }
177
178 /** {@inheritDoc} */
179 @Override
180 public AbsoluteDate getDate() {
181 return date;
182 }
183
184 /** Replace the instance with a data transfer object for serialization.
185 * @return data transfer object that will be serialized
186 */
187 private Object writeReplace() {
188 return new DataTransferObject(weekNumber, milliInWeek);
189 }
190
191 /** Internal class used only for serialization. */
192 private static class DataTransferObject implements Serializable {
193
194 /** Serializable UID. */
195 private static final long serialVersionUID = 20180633L;
196
197 /** Week number since {@link AbsoluteDate#GPS_EPOCH GPS epoch}. */
198 private final int weekNumber;
199
200 /** Number of milliseconds since week start. */
201 private final double milliInWeek;
202
203 /** Simple constructor.
204 * @param weekNumber week number since {@link AbsoluteDate#GPS_EPOCH GPS epoch}
205 * @param milliInWeek number of milliseconds since week start
206 */
207 DataTransferObject(final int weekNumber, final double milliInWeek) {
208 this.weekNumber = weekNumber;
209 this.milliInWeek = milliInWeek;
210 }
211
212 /** Replace the deserialized data transfer object with a {@link GPSDate}.
213 * @return replacement {@link GPSDate}
214 */
215 private Object readResolve() {
216 return new GPSDate(weekNumber, milliInWeek);
217 }
218
219 }
220
221 }