Commit d81e2a71 authored by Luc Maisonobe's avatar Luc Maisonobe

Fixed a display error for dates less than 0.5ms before a leap second.

parent eed3675f
......@@ -951,7 +951,7 @@ public class AbsoluteDate
* in ISO-8601 format with milliseconds accuracy
*/
public String toString(final TimeScale timeScale) {
return getComponents(timeScale).toString(timeScale.insideLeap(this));
return getComponents(timeScale).toString(timeScale.minuteDuration(this));
}
/** Get a String representation of the instant location for a local time.
......@@ -964,8 +964,8 @@ public class AbsoluteDate
*/
public String toString(final int minutesFromUTC)
throws OrekitException {
final boolean inLeap = TimeScalesFactory.getUTC().insideLeap(this);
return getComponents(minutesFromUTC).toString(inLeap);
final int minuteDuration = TimeScalesFactory.getUTC().minuteDuration(this);
return getComponents(minutesFromUTC).toString(minuteDuration);
}
/** Get a String representation of the instant location for a time zone.
......@@ -977,8 +977,8 @@ public class AbsoluteDate
*/
public String toString(final TimeZone timeZone)
throws OrekitException {
final boolean inLeap = TimeScalesFactory.getUTC().insideLeap(this);
return getComponents(timeZone).toString(inLeap);
final int minuteDuration = TimeScalesFactory.getUTC().minuteDuration(this);
return getComponents(timeZone).toString(minuteDuration);
}
}
......@@ -224,17 +224,18 @@ public class DateTimeComponents implements Serializable, Comparable<DateTimeComp
* @return string representation of this pair
*/
public String toString() {
return toString(false);
return toString(60);
}
/** Return a string representation of this pair.
* <p>The format used is ISO8601.</p>
* @param inLeap if true the date is a UTC date during a leap second introduction
* @param minuteDuration 60 or 61 depending on the date being
* close to a leap second introduction
* @return string representation of this pair
*/
public String toString(final boolean inLeap) {
public String toString(final int minuteDuration) {
double second = time.getSecond();
final double wrap = inLeap ? 60.9995 : 59.9995;
final double wrap = minuteDuration - 0.0005;
if (second >= wrap) {
// we should wrap around next millisecond
int minute = time.getMinute();
......
......@@ -47,11 +47,13 @@ public class GLONASSScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return OFFSET + utc.offsetFromTAI(date);
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
final DateTimeComponents utcComponents =
new DateTimeComponents(new DateTimeComponents(date, time), -OFFSET);
......@@ -59,11 +61,19 @@ public class GLONASSScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public boolean insideLeap(final AbsoluteDate date) {
return utc.insideLeap(date);
}
/** {@inheritDoc} */
@Override
public int minuteDuration(final AbsoluteDate date) {
return utc.minuteDuration(date);
}
/** {@inheritDoc} */
@Override
public double getLeap(final AbsoluteDate date) {
return utc.getLeap(date);
}
......
......@@ -69,6 +69,7 @@ public class GMSTScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
// julian seconds since reference date
......@@ -88,26 +89,6 @@ public class GMSTScale implements TimeScale {
}
/** {@inheritDoc} */
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
final AbsoluteDate reference = new AbsoluteDate(date, time, TimeScalesFactory.getTAI());
double offset = 0;
for (int i = 0; i < 8; i++) {
offset = -offsetFromTAI(reference.shiftedBy(offset));
}
return offset;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "GMST";
......
......@@ -34,25 +34,17 @@ public class GPSScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return -19;
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
return 19;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "GPS";
......
......@@ -41,25 +41,17 @@ public class GalileoScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return -19;
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
return 19;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "GST";
......
......@@ -38,25 +38,17 @@ public class QZSSScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return -19;
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
return 19;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "QZSS";
......
......@@ -33,25 +33,17 @@ public class TAIScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate taiTime) {
return 0;
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
return 0;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "TAI";
......
......@@ -55,30 +55,11 @@ public class TCBScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return tdb.offsetFromTAI(date) + LB_RATE * date.durationFrom(REFERENCE_DATE);
}
/** {@inheritDoc} */
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
final AbsoluteDate reference = new AbsoluteDate(date, time, TimeScalesFactory.getTAI());
double offset = 0;
for (int i = 0; i < 3; i++) {
offset = -offsetFromTAI(reference.shiftedBy(offset));
}
return offset;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "TCB";
......
......@@ -54,30 +54,11 @@ public class TCGScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return TT_OFFSET + LG_RATE * date.durationFrom(REFERENCE_DATE);
}
/** {@inheritDoc} */
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
final AbsoluteDate reference = new AbsoluteDate(date, time, TimeScalesFactory.getTAI());
double offset = 0;
for (int i = 0; i < 3; i++) {
offset = -offsetFromTAI(reference.shiftedBy(offset));
}
return offset;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "TCG";
......
......@@ -42,32 +42,13 @@ public class TDBScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
final double dtDays = date.durationFrom(AbsoluteDate.J2000_EPOCH) / Constants.JULIAN_DAY;
final double g = FastMath.toRadians(357.53 + 0.9856003 * dtDays);
return TimeScalesFactory.getTT().offsetFromTAI(date) + (0.001658 * FastMath.sin(g) + 0.000014 * FastMath.sin(2 * g));
}
/** {@inheritDoc} */
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
final AbsoluteDate reference = new AbsoluteDate(date, time, TimeScalesFactory.getTAI());
double offset = 0;
for (int i = 0; i < 3; i++) {
offset = -offsetFromTAI(reference.shiftedBy(offset));
}
return offset;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "TDB";
......
......@@ -36,25 +36,17 @@ public class TTScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
return 32.184;
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
return -32.184;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "TT";
......
......@@ -43,7 +43,14 @@ public interface TimeScale extends Serializable {
* to get a location in <em>{@link TAIScale} time scale</em>
* @see #offsetFromTAI(AbsoluteDate)
*/
double offsetToTAI(final DateComponents date, final TimeComponents time);
default double offsetToTAI(final DateComponents date, final TimeComponents time) {
final AbsoluteDate reference = new AbsoluteDate(date, time, TimeScalesFactory.getTAI());
double offset = 0;
for (int i = 0; i < 8; i++) {
offset = -offsetFromTAI(reference.shiftedBy(offset));
}
return offset;
}
/** Check if date is within a leap second introduction <em>in this time scale</em>.
* <p>
......@@ -54,7 +61,24 @@ public interface TimeScale extends Serializable {
* @param date date to check
* @return true if time is within a leap second introduction
*/
boolean insideLeap(final AbsoluteDate date);
default boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** Check length of the current minute <em>in this time scale</em>.
* <p>
* This method will return 60 for all time scales that do <em>not</em>
* implement leap seconds, even if the date corresponds to a leap second
* in {@link UTCScale UTC scale}, and 61 for time scales that do implement
* leap second when the current date is within the last minute before the
* leap, or during the leap itself.
* </p>
* @param date date to check
* @return 60 or 61 depending on leap seconds introduction
*/
default int minuteDuration(final AbsoluteDate date) {
return 60;
}
/** Get the value of the previous leap.
* <p>
......@@ -64,7 +88,9 @@ public interface TimeScale extends Serializable {
* @param date date to check
* @return value of the previous leap
*/
double getLeap(final AbsoluteDate date);
default double getLeap(final AbsoluteDate date) {
return 0;
}
/** Get the name time scale.
* @return name of the time scale
......
......@@ -55,32 +55,13 @@ public class UT1Scale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
final double dtu1 = eopHistory == null ? 0 : eopHistory.getUT1MinusUTC(date);
final double utcMinusTai = utc.offsetFromTAI(date);
return utcMinusTai + dtu1;
}
/** {@inheritDoc} */
public double offsetToTAI(final DateComponents date, final TimeComponents time) {
final AbsoluteDate reference = new AbsoluteDate(date, time, TimeScalesFactory.getTAI());
double offset = 0;
for (int i = 0; i < 3; i++) {
offset = -offsetFromTAI(reference.shiftedBy(offset));
}
return offset;
}
/** {@inheritDoc} */
public boolean insideLeap(final AbsoluteDate date) {
return false;
}
/** {@inheritDoc} */
public double getLeap(final AbsoluteDate date) {
return 0;
}
/** {@inheritDoc} */
public String getName() {
return "UT1";
......
......@@ -123,17 +123,19 @@ public class UTCScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public double offsetFromTAI(final AbsoluteDate date) {
final UTCTAIOffset offset = findOffset(date);
if (offset == null) {
final int offsetIndex = findOffsetIndex(date);
if (offsetIndex < 0) {
// the date is before the first known leap
return 0;
} else {
return -offset.getOffset(date);
return -offsets[offsetIndex].getOffset(date);
}
}
/** {@inheritDoc} */
@Override
public double offsetToTAI(final DateComponents date,
final TimeComponents time) {
......@@ -180,35 +182,60 @@ public class UTCScale implements TimeScale {
}
/** {@inheritDoc} */
@Override
public boolean insideLeap(final AbsoluteDate date) {
final UTCTAIOffset offset = findOffset(date);
if (offset == null) {
final int offsetIndex = findOffsetIndex(date);
if (offsetIndex < 0) {
// the date is before the first known leap
return false;
} else {
return date.compareTo(offset.getValidityStart()) < 0;
return date.compareTo(offsets[offsetIndex].getValidityStart()) < 0;
}
}
/** Get the value of the previous leap.
* @param date date to check
* @return value of the previous leap
*/
/** {@inheritDoc} */
@Override
public int minuteDuration(final AbsoluteDate date) {
final int offsetIndex = findOffsetIndex(date);
if (offsetIndex < 0) {
// the date is before the first known leap
return 60;
} else {
// TODO: this does not work in the last minute, only in the last second
if (date.compareTo(offsets[offsetIndex].getValidityStart()) < 0) {
// the date is during the leap itself
return 61;
} else {
// the date is after a leap, but it may be just before the next one
if (offsetIndex + 1 < offsets.length &&
offsets[offsetIndex + 1].getDate().durationFrom(date) <= 60.0) {
// the next leap will start in one minute, it will extend the current minute
return 61;
} else {
// no leap is expected within the next minute
return 60;
}
}
}
}
/** {@inheritDoc} */
@Override
public double getLeap(final AbsoluteDate date) {
final UTCTAIOffset offset = findOffset(date);
if (offset == null) {
final int offsetIndex = findOffsetIndex(date);
if (offsetIndex < 0) {
// the date is before the first known leap
return 0;
} else {
return offset.getLeap();
return offsets[offsetIndex].getLeap();
}
}
/** Find the offset valid at some date.
/** Find the index of the offset valid at some date.
* @param date date at which offset is requested
* @return offset valid at this date, or null if date is before first offset.
* @return index of the offset valid at this date, or -1 if date is before first offset.
*/
private UTCTAIOffset findOffset(final AbsoluteDate date) {
private int findOffsetIndex(final AbsoluteDate date) {
int inf = 0;
int sup = offsets.length;
while (sup - inf > 1) {
......@@ -221,12 +248,12 @@ public class UTCScale implements TimeScale {
}
if (sup == offsets.length) {
// the date is after the last known leap second
return offsets[offsets.length - 1];
return offsets.length - 1;
} else if (date.compareTo(offsets[inf].getDate()) < 0) {
// the date is before the first known leap
return null;
return -1;
} else {
return offsets[inf];
return inf;
}
}
......
......@@ -22,6 +22,9 @@
<body>
<release version="8.0" date="TBC"
description="TBC">
<action dev="luc" type="fix">
Fixed a display error for dates less than 0.5ms before a leap second.
</action>
<action dev="luc" type="update">
Use ParameterDriver with scale factor for both orbit determination, conversion,
and partial derivatives computation when finite differences are needed.
......
......@@ -753,6 +753,20 @@ public class AbsoluteDateTest {
Assert.assertEquals("23:59:60.500", t.shiftedBy(+0.5).toString(utc).substring(11));
}
@Test
public void testWrapBeforeLeap() throws OrekitException {
UTCScale utc = TimeScalesFactory.getUTC();
AbsoluteDate t = new AbsoluteDate("2015-06-30T23:59:59.999999", utc);
Assert.assertEquals(2015, t.getComponents(utc).getDate().getYear());
Assert.assertEquals( 6, t.getComponents(utc).getDate().getMonth());
Assert.assertEquals( 30, t.getComponents(utc).getDate().getDay());
Assert.assertEquals( 23, t.getComponents(utc).getTime().getHour());
Assert.assertEquals( 59, t.getComponents(utc).getTime().getMinute());
Assert.assertEquals( 59.999999, t.getComponents(utc).getTime().getSecond(), 1.0e-6);
Assert.assertEquals("2015-06-30T23:59:60.000", t.toString(utc));
Assert.assertEquals("2015-07-01T02:59:60.000", t.toString(TimeScalesFactory.getGLONASS()));
}
@Before
public void setUp() throws OrekitException {
Utils.setDataRoot("regular-data");
......
......@@ -70,23 +70,44 @@ public class GLONASSScaleTest {
AbsoluteDate d = new AbsoluteDate(new DateComponents(1983, 06, 30),
new TimeComponents(23, 59, 59),
TimeScalesFactory.getUTC());
Assert.assertEquals("1983-07-01T02:58:59.000", d.shiftedBy(-60).toString(glonass));
Assert.assertEquals(60, glonass.minuteDuration(d.shiftedBy(-60)));
Assert.assertFalse(glonass.insideLeap(d.shiftedBy(-60)));
Assert.assertEquals("1983-07-01T02:59:59.000", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertFalse(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T02:59:59.251", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertFalse(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T02:59:59.502", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertFalse(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T02:59:59.753", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertFalse(glonass.insideLeap(d));
d = d.shiftedBy( 0.251);
Assert.assertEquals("1983-07-01T02:59:60.004", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertTrue(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T02:59:60.255", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertTrue(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T02:59:60.506", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertTrue(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T02:59:60.757", d.toString(glonass));
Assert.assertEquals(61, glonass.minuteDuration(d));
Assert.assertTrue(glonass.insideLeap(d));
d = d.shiftedBy(0.251);
Assert.assertEquals("1983-07-01T03:00:00.008", d.toString(glonass));
Assert.assertEquals(60, glonass.minuteDuration(d));
Assert.assertFalse(glonass.insideLeap(d));
}
@Test
......@@ -100,6 +121,29 @@ public class GLONASSScaleTest {
}
}
@Test
public void testWrapBeforeLeap() throws OrekitException {
AbsoluteDate t = new AbsoluteDate("2015-07-01T02:59:59.999999", glonass);
Assert.assertEquals("2015-07-01T02:59:60.000", t.toString(glonass));
}
@Test
public void testMinuteDuration() {
final AbsoluteDate t0 = new AbsoluteDate("1983-07-01T02:58:59.000", glonass);
for (double dt = 0; dt < 63; dt += 0.3) {
if (dt < 1.0) {
// before the minute of the leap
Assert.assertEquals(60, glonass.minuteDuration(t0.shiftedBy(dt)));
} else if (dt < 62.0) {
// during the minute of the leap
Assert.assertEquals(61, glonass.minuteDuration(t0.shiftedBy(dt)));
} else {
// after the minute of the leap
Assert.assertEquals(60, glonass.minuteDuration(t0.shiftedBy(dt)));
}
}
}
@Before
public void setUp() throws OrekitException {
Utils.setDataRoot("regular-data");
......
......@@ -63,9 +63,9 @@ public class GMSTScaleTest {
new TimeComponents(23, 59, 59),
utc);
final AbsoluteDate during = before.shiftedBy(1.25);
Assert.assertTrue(utc.insideLeap(during));
Assert.assertEquals(61, utc.minuteDuration(during));
Assert.assertEquals(1.0, utc.getLeap(during), 1.0e-10);
Assert.assertFalse(scale.insideLeap(during));
Assert.assertEquals(60, scale.minuteDuration(during));
Assert.assertEquals(0.0, scale.getLeap(during), 1.0e-10);
}
......
......@@ -76,9 +76,9 @@ public class GPSScaleTest {
new TimeComponents(23, 59, 59),
utc);
final AbsoluteDate during = before.shiftedBy(1.25);
Assert.assertTrue(utc.insideLeap(during));
Assert.assertEquals(61, utc.minuteDuration(during));
Assert.assertEquals(1.0, utc.getLeap(during), 1.0e-10);
Assert.assertFalse(scale.insideLeap(during));
Assert.assertEquals(60, scale.minuteDuration(during));
Assert.assertEquals(0.0, scale.getLeap(during), 1.0e-10);