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 }