Commit 9887e978 authored by Luc Maisonobe's avatar Luc Maisonobe Committed by Maxime Journot
Browse files

Fixed date offsets just before whole seconds.

Fixes #935
Fixes #939
parent e1e1bbe7
......@@ -16,6 +16,8 @@
*/
package org.orekit.propagation.events;
import java.util.function.DoubleFunction;
import org.hipparchus.analysis.UnivariateFunction;
import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
import org.hipparchus.analysis.solvers.BracketedUnivariateSolver.Interval;
......@@ -327,31 +329,29 @@ public class EventState<T extends EventDetector> {
final AbsoluteDate fT0 = loopT;
final double tbDouble = tb.durationFrom(fT0);
final double middle = 0.5 * tbDouble;
final UnivariateFunction f = dt -> {
final DoubleFunction<AbsoluteDate> date = dt -> {
// use either fT0 or tb as the base time for shifts
// in order to ensure we reproduce exactly those times
// using only one reference time like fT0 would imply
// to use ft0.shiftedBy(tbDouble), which may be different
// from tb due to numerical noise (see issue 921)
final AbsoluteDate t;
if (forward == dt <= middle) {
// use start of interval as reference
t = fT0.shiftedBy(dt);
return fT0.shiftedBy(dt);
} else {
// use end of interval as reference
t = tb.shiftedBy(dt - tbDouble);
return tb.shiftedBy(dt - tbDouble);
}
return g(interpolator.getInterpolatedState(t));
};
// tb as a double for use in f
final UnivariateFunction f = dt -> g(interpolator.getInterpolatedState(date.apply(dt)));
if (forward) {
try {
final Interval interval =
solver.solveInterval(maxIterationCount, f, 0, tbDouble);
beforeRootT = fT0.shiftedBy(interval.getLeftAbscissa());
beforeRootT = date.apply(interval.getLeftAbscissa());
beforeRootG = interval.getLeftValue();
afterRootT = fT0.shiftedBy(interval.getRightAbscissa());
afterRootG = interval.getRightValue();
afterRootT = date.apply(interval.getRightAbscissa());
afterRootG = interval.getRightValue();
// CHECKSTYLE: stop IllegalCatch check
} catch (RuntimeException e) {
// CHECKSTYLE: resume IllegalCatch check
......@@ -362,10 +362,10 @@ public class EventState<T extends EventDetector> {
try {
final Interval interval =
solver.solveInterval(maxIterationCount, f, tbDouble, 0);
beforeRootT = fT0.shiftedBy(interval.getRightAbscissa());
beforeRootT = date.apply(interval.getRightAbscissa());
beforeRootG = interval.getRightValue();
afterRootT = fT0.shiftedBy(interval.getLeftAbscissa());
afterRootG = interval.getLeftValue();
afterRootT = date.apply(interval.getLeftAbscissa());
afterRootG = interval.getLeftValue();
// CHECKSTYLE: stop IllegalCatch check
} catch (RuntimeException e) {
// CHECKSTYLE: resume IllegalCatch check
......
......@@ -16,8 +16,10 @@
*/
package org.orekit.propagation.events;
import org.hipparchus.Field;
import java.util.function.DoubleFunction;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.Field;
import org.hipparchus.analysis.UnivariateFunction;
import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
import org.hipparchus.analysis.solvers.BracketedUnivariateSolver.Interval;
......@@ -331,19 +333,31 @@ public class FieldEventState<D extends FieldEventDetector<T>, T extends Calculus
// both non-zero, the usual case, use a root finder.
// time zero for evaluating the function f. Needs to be final
final FieldAbsoluteDate<T> fT0 = loopT;
final UnivariateFunction f = dt -> {
return g(interpolator.getInterpolatedState(fT0.shiftedBy(dt))).getReal();
final double tbDouble = tb.durationFrom(fT0).getReal();
final double middle = 0.5 * tbDouble;
final DoubleFunction<FieldAbsoluteDate<T>> date = dt -> {
// use either fT0 or tb as the base time for shifts
// in order to ensure we reproduce exactly those times
// using only one reference time like fT0 would imply
// to use ft0.shiftedBy(tbDouble), which may be different
// from tb due to numerical noise (see issue 921)
if (forward == dt <= middle) {
// use start of interval as reference
return fT0.shiftedBy(dt);
} else {
// use end of interval as reference
return tb.shiftedBy(dt - tbDouble);
}
};
// tb as a double for use in f
final T tbDouble = tb.durationFrom(fT0);
final UnivariateFunction f = dt -> g(interpolator.getInterpolatedState(date.apply(dt))).getReal();
if (forward) {
try {
final Interval interval =
solver.solveInterval(maxIterationCount, f, 0, tbDouble.getReal());
beforeRootT = fT0.shiftedBy(interval.getLeftAbscissa());
solver.solveInterval(maxIterationCount, f, 0, tbDouble);
beforeRootT = date.apply(interval.getLeftAbscissa());
beforeRootG = zero.add(interval.getLeftValue());
afterRootT = fT0.shiftedBy(interval.getRightAbscissa());
afterRootG = zero.add(interval.getRightValue());
afterRootT = date.apply(interval.getRightAbscissa());
afterRootG = zero.add(interval.getRightValue());
// CHECKSTYLE: stop IllegalCatch check
} catch (RuntimeException e) {
// CHECKSTYLE: resume IllegalCatch check
......@@ -353,11 +367,11 @@ public class FieldEventState<D extends FieldEventDetector<T>, T extends Calculus
} else {
try {
final Interval interval =
solver.solveInterval(maxIterationCount, f, tbDouble.getReal(), 0);
beforeRootT = fT0.shiftedBy(interval.getRightAbscissa());
solver.solveInterval(maxIterationCount, f, tbDouble, 0);
beforeRootT = date.apply(interval.getRightAbscissa());
beforeRootG = zero.add(interval.getRightValue());
afterRootT = fT0.shiftedBy(interval.getLeftAbscissa());
afterRootG = zero.add(interval.getLeftValue());
afterRootT = date.apply(interval.getLeftAbscissa());
afterRootG = zero.add(interval.getLeftValue());
// CHECKSTYLE: stop IllegalCatch check
} catch (RuntimeException e) {
// CHECKSTYLE: resume IllegalCatch check
......
......@@ -314,10 +314,21 @@ public class AbsoluteDate
// Use 2Sum for high precision.
final SumAndResidual sumAndResidual = MathUtils.twoSum(seconds, tsOffset);
final long dl = (long) FastMath.floor(sumAndResidual.getSum());
final double regularOffset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
offset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
time.getMinute() - time.getMinutesFromUTC() - 720l) + dl;
if (regularOffset >= 0) {
// regular case, the offset is between 0.0 and 1.0
offset = regularOffset;
epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
time.getMinute() - time.getMinutesFromUTC() - 720l) + dl;
} else {
// very rare case, the offset is just before a whole second
// we will loose some bits of accuracy when adding 1 second
// but this will ensure the offset remains in the [0.0; 1.0] interval
offset = 1.0 + regularOffset;
epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
time.getMinute() - time.getMinutesFromUTC() - 720l) + dl - 1;
}
}
......@@ -429,8 +440,18 @@ public class AbsoluteDate
epoch = (sumAndResidual.getSum() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
} else {
final long dl = (long) FastMath.floor(sumAndResidual.getSum());
offset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
epoch = since.epoch + dl;
final double regularOffset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
if (regularOffset >= 0) {
// regular case, the offset is between 0.0 and 1.0
offset = regularOffset;
epoch = since.epoch + dl;
} else {
// very rare case, the offset is just before a whole second
// we will loose some bits of accuracy when adding 1 second
// but this will ensure the offset remains in the [0.0; 1.0] interval
offset = 1.0 + regularOffset;
epoch = since.epoch + dl - 1;
}
}
}
......
......@@ -161,8 +161,18 @@ public class FieldAbsoluteDate<T extends CalculusFieldElement<T>>
epoch = (sumAndResidual.getSum().getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
} else {
final long dl = (long) FastMath.floor(sumAndResidual.getSum().getReal());
offset = sumAndResidual.getSum().subtract(dl).add(sumAndResidual.getResidual());
epoch = since.epoch + dl;
final T regularOffset = sumAndResidual.getSum().subtract(dl).add(sumAndResidual.getResidual());
if (regularOffset.getReal() >= 0) {
// regular case, the offset is between 0.0 and 1.0
offset = regularOffset;
epoch = since.epoch + dl;
} else {
// very rare case, the offset is just before a whole second
// we will loose some bits of accuracy when adding 1 second
// but this will ensure the offset remains in the [0.0; 1.0] interval
offset = regularOffset.add(1.0);
epoch = since.epoch + dl - 1;
}
}
}
......@@ -208,11 +218,20 @@ public class FieldAbsoluteDate<T extends CalculusFieldElement<T>>
// Use 2Sum for high precision.
final SumAndResidual sumAndResidual = MathUtils.twoSum(seconds, tsOffset);
final long dl = (long) FastMath.floor(sumAndResidual.getSum());
offset = field.getZero().add((sumAndResidual.getSum() - dl) + sumAndResidual.getResidual());
epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
time.getMinute() - time.getMinutesFromUTC() - 720l) + dl;
final T regularOffset = field.getZero().add((sumAndResidual.getSum() - dl) + sumAndResidual.getResidual());
if (regularOffset.getReal() >= 0) {
// regular case, the offset is between 0.0 and 1.0
offset = regularOffset;
epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
time.getMinute() - time.getMinutesFromUTC() - 720l) + dl;
} else {
// very rare case, the offset is just before a whole second
// we will loose some bits of accuracy when adding 1 second
// but this will ensure the offset remains in the [0.0; 1.0] interval
offset = regularOffset.add(1.0);
epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
time.getMinute() - time.getMinutesFromUTC() - 720l) + dl - 1;
}
this.field = field;
}
......@@ -366,11 +385,21 @@ public class FieldAbsoluteDate<T extends CalculusFieldElement<T>>
final FieldSumAndResidual<T> sumAndResidual = MathUtils.twoSum(field.getZero().add(tA), tB);
if (Double.isInfinite(sumAndResidual.getSum().getReal())) {
this.offset = sumAndResidual.getSum();
this.epoch = (sumAndResidual.getSum().getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
this.epoch = (sumAndResidual.getSum().getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
} else {
final long dl = (long) FastMath.floor(sumAndResidual.getSum().getReal());
this.offset = sumAndResidual.getSum().subtract(dl).add(sumAndResidual.getResidual());
this.epoch = epoch + dl;
final T regularOffset = sumAndResidual.getSum().subtract(dl).add(sumAndResidual.getResidual());
if (regularOffset.getReal() >= 0) {
// regular case, the offset is between 0.0 and 1.0
this.offset = regularOffset;
this.epoch = epoch + dl;
} else {
// very rare case, the offset is just before a whole second
// we will loose some bits of accuracy when adding 1 second
// but this will ensure the offset remains in the [0.0; 1.0) interval
this.offset = regularOffset.add(1.0);
this.epoch = epoch + dl - 1;
}
}
}
......
......@@ -16,6 +16,10 @@
*/
package org.orekit.propagation.events;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.ode.events.Action;
import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
......@@ -30,9 +34,13 @@ import org.orekit.frames.FramesFactory;
import org.orekit.orbits.EquinoctialOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.OrbitType;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.tle.TLE;
import org.orekit.propagation.analytical.tle.TLEPropagator;
import org.orekit.propagation.events.handlers.ContinueOnEvent;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.propagation.events.handlers.StopOnEvent;
import org.orekit.propagation.integration.AdditionalDerivativesProvider;
import org.orekit.propagation.integration.CombinedDerivatives;
import org.orekit.propagation.numerical.NumericalPropagator;
......@@ -178,6 +186,49 @@ public class DateDetectorTest {
Assert.assertEquals(dt, finalState.getDate().durationFrom(iniDate), threshold);
}
@Test
public void testIssue935() {
// startTime, endTime
long start = 1570802400000L;
long end = 1570838399000L;
// Build propagator
TLE tle = new TLE("1 43197U 18015F 19284.07336221 .00000533 00000-0 24811-4 0 9998",
"2 43197 97.4059 50.1428 0017543 265.5429 181.0400 15.24136761 93779");
Propagator propagator = TLEPropagator.selectExtrapolator(tle);
// Max check to seconds
int maxCheck = (int) ((end - start) / 2000);
DateDetector dateDetector = new DateDetector(maxCheck, 1.0e-6,
getAbsoluteDateFromTimestamp(start)).
withHandler(new StopOnEvent<>());
dateDetector.addEventDate(getAbsoluteDateFromTimestamp(end));
// Add event detectors to orbit
propagator.addEventDetector(dateDetector);
// Propagate
final AbsoluteDate startDate = getAbsoluteDateFromTimestamp(start);
final AbsoluteDate endDate = getAbsoluteDateFromTimestamp(end);
SpacecraftState lastState = propagator.propagate(startDate, endDate.shiftedBy(1));
Assert.assertEquals(0.0, lastState.getDate().durationFrom(endDate), 1.0e-15);
}
public static AbsoluteDate getAbsoluteDateFromTimestamp(final long timestamp) {
LocalDateTime utcDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp),
ZoneId.of("UTC"));
int year = utcDate.getYear();
int month = utcDate.getMonthValue();
int day = utcDate.getDayOfMonth();
int hour = utcDate.getHour();
int minute = utcDate.getMinute();
double second = utcDate.getSecond();
double millis = utcDate.getNano() / 1e9;
return new AbsoluteDate(year, month, day, hour, minute, second, TimeScalesFactory.getUTC()).shiftedBy(millis);
}
@Before
public void setUp() {
try {
......
......@@ -17,6 +17,9 @@
package org.orekit.propagation.events;
import java.lang.reflect.Array;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.Field;
......@@ -34,9 +37,13 @@ import org.orekit.frames.FramesFactory;
import org.orekit.orbits.FieldEquinoctialOrbit;
import org.orekit.orbits.FieldOrbit;
import org.orekit.orbits.OrbitType;
import org.orekit.propagation.FieldPropagator;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.analytical.tle.FieldTLE;
import org.orekit.propagation.analytical.tle.FieldTLEPropagator;
import org.orekit.propagation.events.handlers.FieldContinueOnEvent;
import org.orekit.propagation.events.handlers.FieldEventHandler;
import org.orekit.propagation.events.handlers.FieldStopOnEvent;
import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider;
import org.orekit.propagation.numerical.FieldNumericalPropagator;
import org.orekit.propagation.sampling.FieldOrekitStepInterpolator;
......@@ -59,23 +66,32 @@ public class FieldDateDetectorTest {
public void testSimpleTimer() {
doTestSimpleTimer(Decimal64Field.getInstance());
}
@Test
public void testEmbeddedTimer() {
doTestEmbeddedTimer(Decimal64Field.getInstance());
}
@Test
public void testAutoEmbeddedTimer() {
doTestAutoEmbeddedTimer(Decimal64Field.getInstance());
}
@Test(expected=IllegalArgumentException.class)
public void testExceptionTimer() {
doTestExceptionTimer(Decimal64Field.getInstance());
}
@Test
public void testGenericHandler() {
doTestGenericHandler(Decimal64Field.getInstance());
}
@Test
public void testIssue935() {
doTestIssue935(Decimal64Field.getInstance());
}
private <T extends CalculusFieldElement<T>> void doTestSimpleTimer(final Field<T> field) {
T zero = field.getZero();
final FieldVector3D<T> position = new FieldVector3D<>(zero.add(-6142438.668), zero.add( 3492467.560), zero.add( -25767.25680));
......@@ -301,6 +317,51 @@ public class FieldDateDetectorTest {
Assert.assertEquals(dt, finalState.getDate().durationFrom(iniDate).getReal(), threshold);
}
private <T extends CalculusFieldElement<T>> void doTestIssue935(Field<T> field) {
// startTime, endTime
long start = 1570802400000L;
long end = 1570838399000L;
// Build propagator
FieldTLE<T> tle = new FieldTLE<>(field,
"1 43197U 18015F 19284.07336221 .00000533 00000-0 24811-4 0 9998",
"2 43197 97.4059 50.1428 0017543 265.5429 181.0400 15.24136761 93779");
FieldPropagator<T> propagator = FieldTLEPropagator.selectExtrapolator(tle, tle.getParameters(field));
// Max check to seconds
int maxCheck = (int) ((end - start) / 2000);
FieldDateDetector<T> dateDetector = new FieldDateDetector<>(field.getZero().newInstance(maxCheck),
field.getZero().newInstance(1.0e-6),
getAbsoluteDateFromTimestamp(field, start)).
withHandler(new FieldStopOnEvent<>());
dateDetector.addEventDate(getAbsoluteDateFromTimestamp(field, end));
// Add event detectors to orbit
propagator.addEventDetector(dateDetector);
// Propagate
final FieldAbsoluteDate<T> startDate = getAbsoluteDateFromTimestamp(field, start);
final FieldAbsoluteDate<T> endDate = getAbsoluteDateFromTimestamp(field, end);
FieldSpacecraftState<T> lastState = propagator.propagate(startDate, endDate.shiftedBy(1));
Assert.assertEquals(0.0, lastState.getDate().durationFrom(endDate).getReal(), 1.0e-15);
}
public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getAbsoluteDateFromTimestamp(final Field<T> field,
final long timestamp) {
LocalDateTime utcDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp),
ZoneId.of("UTC"));
int year = utcDate.getYear();
int month = utcDate.getMonthValue();
int day = utcDate.getDayOfMonth();
int hour = utcDate.getHour();
int minute = utcDate.getMinute();
double second = utcDate.getSecond();
double millis = utcDate.getNano() / 1e9;
return new FieldAbsoluteDate<>(field, year, month, day, hour, minute, second, TimeScalesFactory.getUTC()).shiftedBy(millis);
}
private <T extends CalculusFieldElement<T>> FieldTimeStamped<T>[] toArray(final FieldAbsoluteDate<T> date) {
@SuppressWarnings("unchecked")
final FieldTimeStamped<T>[] array = (FieldTimeStamped<T>[]) Array.newInstance(FieldTimeStamped.class, 1);
......
......@@ -18,6 +18,7 @@ package org.orekit.time;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
......@@ -1373,6 +1374,45 @@ public class AbsoluteDateTest {
CoreMatchers.is("(-9223372036854775779 + 3.0E300) seconds past epoch"));
}
@Test
public void testNegativeOffsetConstructor() {
try {
AbsoluteDate date = new AbsoluteDate(2019, 10, 11, 20, 40,
FastMath.scalb(6629298651489277.0, -55),
TimeScalesFactory.getTT());
AbsoluteDate after = date.shiftedBy(Precision.EPSILON);
Field epochField = AbsoluteDate.class.getDeclaredField("epoch");
epochField.setAccessible(true);
Field offsetField = AbsoluteDate.class.getDeclaredField("offset");
offsetField.setAccessible(true);
Assert.assertEquals(624098367L, epochField.getLong(date));
Assert.assertEquals(FastMath.nextAfter(1.0, Double.NEGATIVE_INFINITY), offsetField.getDouble(date), 1.0e-20);
Assert.assertEquals(Precision.EPSILON, after.durationFrom(date), 1.0e-20);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
Assert.fail(e.getLocalizedMessage());
}
}
@Test
public void testNegativeOffsetShift() {
try {
AbsoluteDate reference = new AbsoluteDate(2019, 10, 11, 20, 40, 1.6667019180022178E-7,
TimeScalesFactory.getTAI());
double dt = FastMath.scalb(6596520010750484.0, -39);
AbsoluteDate shifted = reference.shiftedBy(dt);
AbsoluteDate after = shifted.shiftedBy(Precision.EPSILON);
Field epochField = AbsoluteDate.class.getDeclaredField("epoch");
epochField.setAccessible(true);
Field offsetField = AbsoluteDate.class.getDeclaredField("offset");
offsetField.setAccessible(true);
Assert.assertEquals(624110398L, epochField.getLong(shifted));
Assert.assertEquals(1.0 - 1.69267e-13, offsetField.getDouble(shifted), 1.0e-15);
Assert.assertEquals(Precision.EPSILON, after.durationFrom(shifted), 1.0e-20);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
Assert.fail(e.getLocalizedMessage());
}
}
@Before
public void setUp() {
Utils.setDataRoot("regular-data");
......
......@@ -287,6 +287,16 @@ public class FieldAbsoluteDateTest {
doTestGetComponentsIssue681and676(Decimal64Field.getInstance());
}
@Test
public void testNegativeOffsetConstructor() {
doTestNegativeOffsetConstructor(Decimal64Field.getInstance());
}
@Test
public void testNegativeOffsetShift() {
doTestNegativeOffsetShift(Decimal64Field.getInstance());
}
private <T extends CalculusFieldElement<T>> void doTestStandardEpoch(final Field<T> field) {
TimeScale tai = TimeScalesFactory.getTAI();
......@@ -1200,6 +1210,46 @@ public class FieldAbsoluteDateTest {
MatcherAssert.assertThat(difference, CoreMatchers.is(Double.NaN));
}
@SuppressWarnings("unchecked")
private <T extends CalculusFieldElement<T>> void doTestNegativeOffsetConstructor(final Field<T> field) {
try {
FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field,
2019, 10, 11, 20, 40,
FastMath.scalb(6629298651489277.0, -55),
TimeScalesFactory.getTT());
FieldAbsoluteDate<T> after = date.shiftedBy(Precision.EPSILON);
java.lang.reflect.Field epochField = FieldAbsoluteDate.class.getDeclaredField("epoch");
epochField.setAccessible(true);
java.lang.reflect.Field offsetField = FieldAbsoluteDate.class.getDeclaredField("offset");
offsetField.setAccessible(true);
Assert.assertEquals(624098367L, epochField.getLong(date));
Assert.assertEquals(FastMath.nextAfter(1.0, Double.NEGATIVE_INFINITY), ((T) offsetField.get(date)).getReal(), 1.0e-20);
Assert.assertEquals(Precision.EPSILON, after.durationFrom(date).getReal(), 1.0e-20);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
Assert.fail(e.getLocalizedMessage());
}
}
@SuppressWarnings("unchecked")
private <T extends CalculusFieldElement<T>> void doTestNegativeOffsetShift(final Field<T> field) {
try {
FieldAbsoluteDate<T> reference = new FieldAbsoluteDate<>(field, 2019, 10, 11, 20, 40, 1.6667019180022178E-7,
TimeScalesFactory.getTAI());
T dt = field.getZero().newInstance(FastMath.scalb(6596520010750484.0, -39));
FieldAbsoluteDate<T> shifted = reference.shiftedBy(dt);
FieldAbsoluteDate<T> after = shifted.shiftedBy(Precision.EPSILON);
java.lang.reflect.Field epochField = FieldAbsoluteDate.class.getDeclaredField("epoch");
epochField.setAccessible(true);
java.lang.reflect.Field offsetField = FieldAbsoluteDate.class.getDeclaredField("offset");
offsetField.setAccessible(true);
Assert.assertEquals(624110398L, epochField.getLong(shifted));
Assert.assertEquals(1.0 - 1.69267e-13, ((T) offsetField.get(shifted)).getReal(), 1.0e-15);
Assert.assertEquals(Precision.EPSILON, after.durationFrom(shifted).getReal(), 1.0e-20);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
Assert.fail(e.getLocalizedMessage());
}
}
private <T extends CalculusFieldElement<T>> void check(FieldAbsoluteDate<T> date,
int year, int month, int day, int hour, int minute, double second,
double roundTripUlps, final int secondUlps, final double absTol) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment