Commit b11a5ca1 authored by Bryan Cazabonne's avatar Bryan Cazabonne
Browse files

Merge branch 'release-11.1'

parents 9f731446 dc818740
Pipeline #2008 passed with stages
in 28 minutes and 42 seconds
......@@ -49,6 +49,8 @@ src/*/resources/*/unx*.405 -text
src/*/resources/*/*/unx*.405 -text
src/*/resources/*/unx*.406 -text
src/*/resources/*/*/unx*.406 -text
src/*/resources/*/lnx*.431 -text
src/*/resources/*/*/lnx*.431 -text
src/*/resources/inpop/*.dat -text
src/*/resources/gnss/ntrip/*.dat -text
......@@ -11,8 +11,8 @@ elements (orbits, dates, attitude, frames, ...) and various algorithms to
handle them (conversions, propagations, pointing, events detection, orbit determination ...).
[![](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)
[![](https://sonar.orekit.org/api/project_badges/measure?project=orekit%3Aorekit&metric=alert_status)](https://sonar.orekit.org/dashboard?id=org.orekit%3Aorekit)
[![](https://sonar.orekit.org/api/project_badges/measure?project=orekit%3Aorekit&metric=coverage)](https://sonar.orekit.org/dashboard?id=org.orekit%3Aorekit)
[![](https://sonar.orekit.org/api/project_badges/measure?project=orekit%3Aorekit&metric=alert_status)](https://sonar.orekit.org/dashboard?id=orekit%3Aorekit)
[![](https://sonar.orekit.org/api/project_badges/measure?project=orekit%3Aorekit&metric=coverage)](https://sonar.orekit.org/dashboard?id=orekit%3Aorekit)
## Download
......
......@@ -2,7 +2,7 @@
<project name="orekit" default="jar" basedir=".">
<property name="project.version" value="11.1.1" />
<property name="project.version" value="11.1.2" />
<property name="src.dir" location="src" />
<property name="main.src.dir" value="${src.dir}/main/java" />
......@@ -18,7 +18,7 @@
<property name="lib.dir" location="lib" />
<property name="hipparchus.version" value="2.0" />
<property name="hipparchus.version" value="2.1" />
<property name="hipparchus.core.jar" value="hipparchus-core-${hipparchus.version}.jar" />
<property name="hipparchus.geometry.jar" value="hipparchus-geometry-${hipparchus.version}.jar" />
<property name="hipparchus.ode.jar" value="hipparchus-ode-${hipparchus.version}.jar" />
......
......@@ -5,7 +5,7 @@
<groupId>org.orekit</groupId>
<artifactId>orekit</artifactId>
<packaging>jar</packaging>
<version>11.1.1</version>
<version>11.1.2</version>
<name>ORbit Extrapolation KIT</name>
<url>http://www.orekit.org/</url>
......@@ -50,7 +50,7 @@
<orekit.maven-install-plugin.version>3.0.0-M1</orekit.maven-install-plugin.version>
<orekit.mathjax.config>&lt;script type=&quot;text/x-mathjax-config&quot;&gt;MathJax.Hub.Config({ TeX: { extensions: [&quot;autoload.js&quot;]}});&lt;/script&gt;</orekit.mathjax.config>
<orekit.mathjax.enable>&lt;script type=&quot;text/javascript&quot; src=&quot;https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_CHTML&quot;&gt;&lt;/script&gt;</orekit.mathjax.enable>
<orekit.hipparchus.version>2.0</orekit.hipparchus.version>
<orekit.hipparchus.version>2.1</orekit.hipparchus.version>
<orekit.junit.version>4.13.2</orekit.junit.version>
<orekit.compiler.source>1.8</orekit.compiler.source>
<orekit.compiler.target>1.8</orekit.compiler.target>
......
......@@ -20,6 +20,54 @@
<title>Orekit Changes</title>
</properties>
<body>
<release version="11.1.2" date="2022-04-27"
description="Version 11.1.2 is a patch release of Orekit.
It fixes issues related to the parsing and writing of CCSDS and ILRS files.
It also fixes issues in ECOM2 solar radiation pressure model, event bracketing,
ephemeris generation, and NTW local orbital frame.
Finally it includes some improvements in the class documentation">
<action dev="luc" type="fix" issue="917">
Fixed missing tags in XML generation by EphemerisWriter.
</action>
<action dev="louis" type="fix" issue="886">
Fixed rollover in CRD parser.
</action>
<action dev="louis" type="fix" issue="786">
Fixed NaNs when constructing Keplerian orbit from PV
computed from KeplerianOrbit.
</action>
<action dev="louis" type="fix" issue="826">
Fixed ephemeris generation using PropagatorParallelizer.
</action>
<action dev="luc" type="fix" issue="921">
Fixed event bracketing problem induced by numerical noise at end of search interval.
</action>
<action dev="luc" type="fix" issue="919">
Fixed ephemeris generation with several derivatives providers.
</action>
<action dev="maxime" type="fix" issue="909">
Fixed wrong implementation of NTW LOF frame.
</action>
<action dev="bryan" type="fix" issue="910">
Fixed eD and eY equation in ECOM2 model.
</action>
<action dev="pascal" type="fix" issue="908">
Fixed unmanaged comment in OMM.
</action>
<action dev="pascal" type="fix" issue="906">
Fixed unmanaged units in OMM.
</action>
<action dev="evan" type="fix" issue="882">
Fix StreamingOemWriter in ITRF and without optional fields.
</action>
<action dev="evan" type="fix" issue="912">
Fix StreamingOemWriter without acceleration.
</action>
<action dev="luc" type="fix" issue="184">
Fixed non-bracketing issue when RESET_STATE slightly moves an event at the start
of a step and another regular event happens in the first half of the same step
</action>
</release>
<release version="11.1.1" date="2022-03-17"
description="Version 11.1.1 is a patch release of Orekit.
It fixes issues related to the parsing of SP3 and Rinex files. It also takes
......
......@@ -1161,7 +1161,7 @@ abstract class PredefinedIAUPoles implements IAUPole {
public static PredefinedIAUPoles getIAUPole(final EphemerisType body,
final TimeScales timeScales) {
switch(body) {
switch (body) {
case SUN :
return new Sun(timeScales);
case MERCURY :
......
......@@ -61,7 +61,8 @@ public class IodGibbs {
* @param p1 First position measurement
* @param p2 Second position measurement
* @param p3 Third position measurement
* @return an initial orbit estimation
* @return an initial orbit estimation at the central date
* (i.e., date of the second position measurement)
* @since 11.0
*/
public KeplerianOrbit estimate(final Frame frame, final Position p1, final Position p2, final Position p3) {
......@@ -78,7 +79,8 @@ public class IodGibbs {
* @param pv1 PV measure 1 taken in frame
* @param pv2 PV measure 2 taken in frame
* @param pv3 PV measure 3 taken in frame
* @return an initial orbit estimation
* @return an initial orbit estimation at the central date
* (i.e., date of the second PV measurement)
*/
public KeplerianOrbit estimate(final Frame frame, final PV pv1, final PV pv2, final PV pv3) {
return estimate(frame,
......@@ -97,7 +99,8 @@ public class IodGibbs {
* @param date2 date of measure 2
* @param r3 position 3 measured in frame
* @param date3 date of measure 3
* @return an initial orbit estimation
* @return an initial orbit estimation at the central date
* (i.e., date of the second position measurement)
*/
public KeplerianOrbit estimate(final Frame frame,
final Vector3D r1, final AbsoluteDate date1,
......
......@@ -131,7 +131,8 @@ public class IodGooding {
* @param rho3init initial guess of the range problem. range 3, in meters
* @param nRev number of complete revolutions between observation 1 and 3
* @param direction true if posigrade (short way)
* @return an estimate of the Keplerian orbit
* @return an estimate of the Keplerian orbit at the central date
* (i.e., date of the second angular observation)
* @since 11.0
*/
public KeplerianOrbit estimate(final Frame frame, final AngularRaDec raDec1,
......@@ -155,7 +156,8 @@ public class IodGooding {
* @param raDec3 third angular observation
* @param rho1init initial guess of the range problem. range 1, in meters
* @param rho3init initial guess of the range problem. range 3, in meters
* @return an estimate of the Keplerian orbit
* @return an estimate of the Keplerian orbit at the central date
* (i.e., date of the second angular observation)
* @since 11.0
*/
public KeplerianOrbit estimate(final Frame frame, final AngularRaDec raDec1,
......@@ -179,7 +181,8 @@ public class IodGooding {
* @param rho3init initial guess of the range problem. range 3, in meters
* @param nRev number of complete revolutions between observation1 and 3
* @param direction true if posigrade (short way)
* @return an estimate of the Keplerian orbit
* @return an estimate of the Keplerian orbit at the central date
* (i.e., date of the second angular observation)
*/
public KeplerianOrbit estimate(final Frame frame, final Vector3D O1, final Vector3D O2, final Vector3D O3,
final Vector3D lineOfSight1, final AbsoluteDate dateObs1,
......@@ -235,7 +238,8 @@ public class IodGooding {
* @param dateObs3 date of observation 1
* @param rho1init initial guess of the range problem. range 1, in meters
* @param rho3init initial guess of the range problem. range 3, in meters
* @return an estimate of the Keplerian orbit
* @return an estimate of the Keplerian orbit at the central date
* (i.e., date of the second angular observation)
*/
public KeplerianOrbit estimate(final Frame frame, final Vector3D O1, final Vector3D O2, final Vector3D O3,
final Vector3D lineOfSight1, final AbsoluteDate dateObs1,
......
......@@ -78,7 +78,7 @@ public class IodLambert {
* @param nRev number of revolutions
* @param p1 first position measurement
* @param p2 second position measurement
* @return an initial orbit estimation
* @return an initial Keplerian orbit estimation at the first observation date t1
* @since 11.0
*/
public KeplerianOrbit estimate(final Frame frame, final boolean posigrade,
......@@ -117,7 +117,7 @@ public class IodLambert {
* @param t1 date of observation 1
* @param p2 position vector 2
* @param t2 date of observation 2
* @return an initial Keplerian orbit estimate
* @return an initial Keplerian orbit estimate at the first observation date t1
*/
public KeplerianOrbit estimate(final Frame frame, final boolean posigrade,
final int nRev,
......
......@@ -60,7 +60,7 @@ public class IodLaplace {
* @param raDec1 first angular observation
* @param raDec2 second angular observation
* @param raDec3 third angular observation
* @return estimate of the orbit at the central date or null if
* @return estimate of the orbit at the central date obsDate2 or null if
* no estimate is possible with the given data
* @since 11.0
*/
......@@ -83,7 +83,7 @@ public class IodLaplace {
* @param los2 line of sight unit vector 2
* @param obsDate3 date of observation 3
* @param los3 line of sight unit vector 3
* @return estimate of the orbit at the central date dateObs2 or null if
* @return estimate of the orbit at the central date obsDate2 or null if
* no estimate is possible with the given data
*/
public CartesianOrbit estimate(final Frame frame, final PVCoordinates obsPva,
......
......@@ -79,6 +79,9 @@ public class Units {
/** Hertz per second unit. */
public static final Unit HZ_PER_S = Unit.parse("Hz/s");
/** Earth radii reciprocal unit. */
public static final Unit ONE_PER_ER = Unit.parse("1/ER");
/** Private constructor for a utility class.
*/
private Units() {
......
......@@ -80,7 +80,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
Rotation rotation = isFirst ?
new Rotation(components[0], components[1], components[2], components[3], true) :
......@@ -142,7 +142,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
FieldRotation<UnivariateDerivative1> rotation =
isFirst ?
new FieldRotation<>(new UnivariateDerivative1(components[0], components[4]),
......@@ -207,7 +207,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
// Build the needed objects
final Rotation rotation = isFirst ?
new Rotation(components[0], components[1], components[2], components[3], true) :
......@@ -257,7 +257,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
// Build the needed objects
Rotation rotation = new Rotation(eulerRotSequence, RotationConvention.FRAME_TRANSFORM,
......@@ -311,7 +311,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
// Build the needed objects
final Rotation rotation = new Rotation(eulerRotSequence,
......@@ -374,7 +374,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
// Build the needed objects
final Rotation rotation = new Rotation(RotationOrder.ZYZ,
......@@ -414,7 +414,7 @@ public enum AttitudeType {
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final AbsoluteDate date,
final double...components) {
final double... components) {
// Attitude parameters in the Specified Reference Frame for a Spin Stabilized Satellite
// are optional in CCSDS AEM format. Support for this attitude type is not implemented
// yet in Orekit.
......@@ -521,7 +521,7 @@ public enum AttitudeType {
*/
public abstract TimeStampedAngularCoordinates build(boolean isFirst, boolean isExternal2SpacecraftBody,
RotationOrder eulerRotSequence, boolean isSpacecraftBodyRate,
AbsoluteDate date, double...components);
AbsoluteDate date, double... components);
/**
* Get the angular derivative filter corresponding to the attitude data.
......
......@@ -173,6 +173,9 @@ public class EphemerisWriter implements EphemerisFileWriter {
metadata.setInterpolationDegree(segment.getInterpolationSamples() - 1);
writer.writeMetadata(generator, metadata);
// we enter data section
writer.startData(generator);
if (segment instanceof OemSegment) {
// write data comments
generator.writeComments(((OemSegment) segment).getData().getComments());
......@@ -193,6 +196,9 @@ public class EphemerisWriter implements EphemerisFileWriter {
writer.writeCovariances(generator, metadata, ((OemSegment) segment).getCovarianceMatrices());
}
// we exit data section
writer.endData(generator);
}
}
......@@ -319,9 +319,12 @@ public class OemWriter extends AbstractMessageWriter<Header, OemSegment, Oem> {
// interpolation
generator.writeEntry(OemMetadataKey.INTERPOLATION.name(), metadata.getInterpolationMethod(), false);
generator.writeEntry(OemMetadataKey.INTERPOLATION_DEGREE.name(),
Integer.toString(metadata.getInterpolationDegree()),
null, false);
// treat degree < 0 as equivalent to null
if (metadata.getInterpolationDegree() >= 0) {
generator.writeEntry(OemMetadataKey.INTERPOLATION_DEGREE.name(),
Integer.toString(metadata.getInterpolationDegree()),
null, false);
}
// Stop metadata
generator.exitSection();
......
......@@ -24,10 +24,12 @@ import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.definitions.FrameFacade;
import org.orekit.files.ccsds.section.Header;
import org.orekit.files.ccsds.utils.generation.Generator;
import org.orekit.frames.Frame;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.sampling.OrekitFixedStepHandler;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.TimeStampedPVCoordinates;
/**
* A writer for OEM files.
......@@ -79,22 +81,80 @@ public class StreamingOemWriter implements AutoCloseable {
/** Current metadata. */
private final OemMetadata metadata;
/** If the propagator's frame should be used. */
private final boolean useAttitudeFrame;
/** If acceleration should be included in the output. */
private final boolean includeAcceleration;
/** Indicator for writing header. */
private boolean headerWritePending;
/** Simple constructor.
/**
* Construct a writer that for each segment uses the reference frame of the
* first state's attitude.
*
* @param generator generator for OEM output
* @param writer writer for the AEM message format
* @param header file header (may be null)
* @param template template for metadata
* @param writer writer for the AEM message format
* @param header file header (may be null)
* @param template template for metadata
* @since 11.0
* @see #StreamingOemWriter(Generator, OemWriter, Header, OemMetadata, boolean)
*/
public StreamingOemWriter(final Generator generator, final OemWriter writer,
final Header header, final OemMetadata template) {
this(generator, writer, header, template, true);
}
/**
* Construct a writer that writes position, velocity, and acceleration at
* each time step.
*
* @param generator generator for OEM output
* @param writer writer for the AEM message format
* @param header file header (may be null)
* @param template template for metadata
* @param useAttitudeFrame if {@code true} then the reference frame for each
* segment is taken from the first state's attitude.
* Otherwise the {@code template}'s reference frame
* is used, {@link OemMetadata#getReferenceFrame()}.
* @see #StreamingOemWriter(Generator, OemWriter, Header, OemMetadata,
* boolean, boolean)
* @since 11.1.2
*/
public StreamingOemWriter(final Generator generator, final OemWriter writer,
final Header header, final OemMetadata template,
final boolean useAttitudeFrame) {
this(generator, writer, header, template, useAttitudeFrame, true);
}
/**
* Simple constructor.
*
* @param generator generator for OEM output
* @param writer writer for the AEM message format
* @param header file header (may be null)
* @param template template for metadata
* @param useAttitudeFrame if {@code true} then the reference frame for
* each segment is taken from the first state's
* attitude. Otherwise the {@code template}'s
* reference frame is used, {@link
* OemMetadata#getReferenceFrame()}.
* @param includeAcceleration if {@code true} then acceleration is included
* in the OEM file produced. Otherwise only
* position and velocity is included.
* @since 11.1.2
*/
public StreamingOemWriter(final Generator generator, final OemWriter writer,
final Header header, final OemMetadata template,
final boolean useAttitudeFrame,
final boolean includeAcceleration) {
this.generator = generator;
this.writer = writer;
this.header = header;
this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
this.useAttitudeFrame = useAttitudeFrame;
this.includeAcceleration = includeAcceleration;
this.headerWritePending = true;
}
......@@ -117,6 +177,9 @@ public class StreamingOemWriter implements AutoCloseable {
/** A writer for a segment of an OEM. */
public class SegmentWriter implements OrekitFixedStepHandler {
/** Reference frame of this segment. */
private Frame frame;
/**
* {@inheritDoc}
*
......@@ -144,7 +207,12 @@ public class StreamingOemWriter implements AutoCloseable {
metadata.setUseableStartTime(null);
metadata.setUseableStopTime(null);
metadata.setStopTime(t);
metadata.setReferenceFrame(FrameFacade.map(s0.getAttitude().getReferenceFrame()));
if (useAttitudeFrame) {
frame = s0.getAttitude().getReferenceFrame();
metadata.setReferenceFrame(FrameFacade.map(frame));
} else {
frame = metadata.getFrame();
}
writer.writeMetadata(generator, metadata);
writer.startData(generator);
} catch (IOException e) {
......@@ -156,7 +224,9 @@ public class StreamingOemWriter implements AutoCloseable {
@Override
public void handleStep(final SpacecraftState currentState) {
try {
writer.writeOrbitEphemerisLine(generator, metadata, currentState.getPVCoordinates(), true);
final TimeStampedPVCoordinates pv =
currentState.getPVCoordinates(frame);
writer.writeOrbitEphemerisLine(generator, metadata, pv, includeAcceleration);
} catch (IOException e) {
throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
}
......
......@@ -19,7 +19,7 @@ package org.orekit.files.ccsds.ndm.odm.omm;
import org.orekit.files.ccsds.definitions.Units;
import org.orekit.files.ccsds.utils.ContextBinding;
import org.orekit.files.ccsds.utils.lexical.ParseToken;
import org.orekit.utils.units.Unit;
import org.orekit.files.ccsds.utils.lexical.TokenType;
/** Keys for {@link OmmTle TLE} entries.
......@@ -28,6 +28,10 @@ import org.orekit.utils.units.Unit;
*/
public enum OmmTleKey {
/** Comment entry. */
COMMENT((token, context, container) ->
token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true),
/** Ephemeris Type, only required if MEAN_ELEMENT_THEORY = SGP/SGP4. */
EPHEMERIS_TYPE((token, context, container) -> token.processAsInteger(container::setEphemerisType)),
......@@ -44,7 +48,7 @@ public enum OmmTleKey {
REV_AT_EPOCH((token, context, container) -> token.processAsInteger(container::setRevAtEpoch)),
/** SGP/SGP4 drag-like coefficient. */
BSTAR((token, context, container) -> token.processAsDouble(Unit.ONE, context.getParsedUnitsBehavior(),
BSTAR((token, context, container) -> token.processAsDouble(Units.ONE_PER_ER, context.getParsedUnitsBehavior(),
container::setBStar)),
/** First time derivative of mean motion. */
......
......@@ -38,6 +38,7 @@ import org.orekit.time.TimeScale;
* Base class for Navigation Data Message (NDM) files.
* @param <H> type of the header
* @param <S> type of the segments
* @param <F> type of the NDM constituent
* @author Luc Maisonobe
* @since 11.0
*/
......
......@@ -125,21 +125,21 @@ public class CRDParser {
final ParseInfo pi = new ParseInfo();
int lineNumber = 0;
Stream<LineParser> cdrParsers = Stream.of(LineParser.H1);
Stream<LineParser> crdParsers = Stream.of(LineParser.H1);
try (BufferedReader reader = new BufferedReader(source.getOpener().openReaderOnce())) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
++lineNumber;
final String l = line;
final Optional<LineParser> selected = cdrParsers.filter(p -> p.canHandle(l)).findFirst();
final Optional<LineParser> selected = crdParsers.filter(p -> p.canHandle(l)).findFirst();
if (selected.isPresent()) {
try {
selected.get().parse(line, pi);
} catch (StringIndexOutOfBoundsException | NumberFormatException e) {
throw new OrekitException(e,
OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
lineNumber, source.getName(), line);
OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
lineNumber, source.getName(), line);
}
cdrParsers = selected.get().allowedNext();
crdParsers = selected.get().allowedNext();
}
if (pi.done) {
// Return file
......@@ -153,6 +153,18 @@ public class CRDParser {
}
/**
* Computes if a day shift has happened comparing the current and past epoch, described by seconds in the day.
* This is useful as the data is sorted in the chronological order inside the file.
*
* @param lastSecOfDay
* @param secOfDay
* @return Boolean true if change in day.
*/
private static int checkRollover(final double lastSecOfDay, final double secOfDay) {
return (secOfDay > lastSecOfDay) ? 0 : 1;
}
/** Transient data used for parsing a CRD file. The data is kept in a
* separate data structure to make the parser thread-safe.
* <p><b>Note</b>: The class intentionally does not provide accessor
......@@ -349,8 +361,8 @@ public class CRDParser {
pi.startEpoch = new DateComponents(yearS, monthS, dayS);
pi.header.setStartEpoch(new AbsoluteDate(yearS, monthS, dayS,
hourS, minuteS, secondS,
pi.timeScale));
hourS, minuteS, secondS,
pi.timeScale));
// End epoch
final int yearE = Integer.parseInt(values[8]);
......@@ -361,8 +373,8 @@ public class CRDParser {
final double secondE = Integer.parseInt(values[13]);
pi.header.setEndEpoch(new AbsoluteDate(yearE, monthE, dayE,
hourE, minuteE, secondE,
pi.timeScale));
hourE, minuteE, secondE,
pi.timeScale));
// Data release
pi.header.setDataReleaseFlag(Integer.parseInt(values[14]));
......@@ -701,6 +713,12 @@ public class CRDParser {
/** Range Record (Full rate, Sampled Engineering/Quicklook). */
TEN("10") {
/** Storage for the last epoch parsed to compare with the current.*/
private double lastSecOfDay = 0;
/** Shift in days due to the rollover.*/