Commit 1ec568b7 authored by Bryan Cazabonne's avatar Bryan Cazabonne
Browse files

Merge branch 'rferme/orekit-issue-705v2' into develop

parents e9b8893e b1403f5a
......@@ -26,6 +26,7 @@ import java.util.Map.Entry;
import org.hipparchus.geometry.euclidean.threed.RotationOrder;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.general.AttitudeEphemerisFile;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScale;
import org.orekit.utils.TimeStampedAngularCoordinates;
......@@ -37,7 +38,7 @@ import org.orekit.utils.TimeStampedAngularCoordinates;
* @author Bryan Cazabonne
* @since 10.2
*/
public class AEMFile extends ADMFile {
public class AEMFile extends ADMFile implements AttitudeEphemerisFile {
/** List of ephemeris blocks. */
private List<AttitudeEphemeridesBlock> attitudeBlocks;
......@@ -77,12 +78,8 @@ public class AEMFile extends ADMFile {
}
}
/**
* Get the attitude loaded ephemeris for each satellite in the file.
* @return a map from the satellite's ID to the information about that satellite
* contained in the file.
*/
/** {@inheritDoc} */
@Override
public Map<String, AemSatelliteEphemeris> getSatellites() {
final Map<String, List<AttitudeEphemeridesBlock>> satellites = new HashMap<>();
for (final AttitudeEphemeridesBlock ephemeridesBlock : attitudeBlocks) {
......@@ -93,16 +90,75 @@ public class AEMFile extends ADMFile {
final Map<String, AemSatelliteEphemeris> ret = new HashMap<>();
for (final Entry<String, List<AttitudeEphemeridesBlock>> entry : satellites.entrySet()) {
final String id = entry.getKey();
ret.put(id, new AemSatelliteEphemeris(entry.getValue()));
ret.put(id, new AemSatelliteEphemeris(id, entry.getValue()));
}
return ret;
}
/** AEM ephemeris blocks for a single satellite. */
public static class AemSatelliteEphemeris implements SatelliteAttitudeEphemeris {
/** ID of the satellite. */
private final String id;
/** The attitude ephemeris data for the satellite. */
private final List<AttitudeEphemeridesBlock> blocks;
/**
* Create a container for the set of ephemeris blocks in the file that pertain to
* a single satellite. The satellite's ID is set to ""
* @param blocks containing ephemeris data for the satellite.
* @deprecated in 10.3, replaced by {@link #AemSatelliteEphemeris(String, List)}
*/
@Deprecated
public AemSatelliteEphemeris(final List<AttitudeEphemeridesBlock> blocks) {
this("", blocks);
}
/**
* Create a container for the set of ephemeris blocks in the file that pertain to
* a single satellite.
* @param id of the satellite.
* @param blocks containing ephemeris data for the satellite.
* @since 10.3
*/
public AemSatelliteEphemeris(final String id, final List<AttitudeEphemeridesBlock> blocks) {
this.id = id;
this.blocks = blocks;
}
/** {@inheritDoc} */
@Override
public String getId() {
return this.id;
}
/** {@inheritDoc} */
@Override
public List<AttitudeEphemeridesBlock> getSegments() {
return Collections.unmodifiableList(blocks);
}
/** {@inheritDoc} */
@Override
public AbsoluteDate getStart() {
return blocks.get(0).getStart();
}
/** {@inheritDoc} */
@Override
public AbsoluteDate getStop() {
return blocks.get(blocks.size() - 1).getStop();
}
}
/**
* The Attitude Ephemerides Blocks class contain metadata
* and the list of attitude data lines.
*/
public class AttitudeEphemeridesBlock {
public class AttitudeEphemeridesBlock implements AttitudeEphemerisSegment {
/** Meta-data for the block. */
private ADMMetaData metaData;
......@@ -174,10 +230,8 @@ public class AEMFile extends ADMFile {
return attitudeDataLines;
}
/**
* Get an unmodifiable list of attitude data lines.
* @return a list of attitude data
*/
/** {@inheritDoc} */
@Override
public List<TimeStampedAngularCoordinates> getAngularCoordinates() {
return Collections.unmodifiableList(this.attitudeDataLines);
}
......@@ -190,20 +244,14 @@ public class AEMFile extends ADMFile {
return metaData;
}
/**
* Get the name of the center of the coordinate system the ephemeris is provided in.
* This may be a natural origin, such as the center of the Earth, another satellite, etc.
* @return the name of the frame center
*/
/** {@inheritDoc} */
@Override
public String getFrameCenterString() {
return this.getMetaData().getCenterName();
}
/**
* Get the reference frame A specifier as it appeared in the file.
* @return the frame name as it appeared in the file (A).
*/
/** {@inheritDoc} */
@Override
public String getRefFrameAString() {
return refFrameAString;
}
......@@ -216,10 +264,8 @@ public class AEMFile extends ADMFile {
this.refFrameAString = frame;
}
/**
* Get the reference frame B specifier as it appeared in the file.
* @return the frame name as it appeared in the file (B).
*/
/** {@inheritDoc} */
@Override
public String getRefFrameBString() {
return this.refFrameBString;
}
......@@ -248,10 +294,8 @@ public class AEMFile extends ADMFile {
this.rateFrameString = frame;
}
/**
* Get the rotation direction of the attitude.
* @return the rotation direction of the attitude
*/
/** {@inheritDoc} */
@Override
public String getAttitudeDirection() {
return attitudeDir;
}
......@@ -264,10 +308,8 @@ public class AEMFile extends ADMFile {
this.attitudeDir = direction;
}
/**
* Get the format of the data lines in the message.
* @return the format of the data lines in the message
*/
/** {@inheritDoc} */
@Override
public String getAttitudeType() {
return attitudeType;
}
......@@ -280,10 +322,8 @@ public class AEMFile extends ADMFile {
this.attitudeType = type;
}
/**
* Get the flag for the placement of the quaternion QC in the attitude data.
* @return true if QC is the first element in the attitude data
*/
/** {@inheritDoc} */
@Override
public boolean isFirst() {
return isFirst;
}
......@@ -312,19 +352,14 @@ public class AEMFile extends ADMFile {
this.eulerRotSeq = eulerRotSeq;
}
/**
* Get the time scale for this data segment.
* @return the time scale identifier, as specified in the file, or
* {@code null} if the data file does not specify a time scale.
*/
/** {@inheritDoc} */
@Override
public String getTimeScaleString() {
return metaData.getTimeSystem().toString();
}
/**
* Get the time scale for this data segment.
* @return the time scale for this data. Never {@code null}.
*/
/** {@inheritDoc} */
@Override
public TimeScale getTimeScale() {
return metaData.getTimeScale();
}
......@@ -393,10 +428,8 @@ public class AEMFile extends ADMFile {
this.useableStopTime = useableStopTime;
}
/**
* Get the start date of this attitude data segment.
* @return attitude data segment start date.
*/
/** {@inheritDoc} */
@Override
public AbsoluteDate getStart() {
// usable start time overrides start time if it is set
final AbsoluteDate start = this.getUseableStartTime();
......@@ -407,10 +440,8 @@ public class AEMFile extends ADMFile {
}
}
/**
* Get the end date of this attitude data segment.
* @return attitude data segment end date.
*/
/** {@inheritDoc} */
@Override
public AbsoluteDate getStop() {
// useable stop time overrides stop time if it is set
final AbsoluteDate stop = this.getUseableStopTime();
......@@ -421,10 +452,8 @@ public class AEMFile extends ADMFile {
}
}
/**
* Get the interpolation method to be used.
* @return the interpolation method
*/
/** {@inheritDoc} */
@Override
public String getInterpolationMethod() {
return interpolationMethod;
}
......@@ -453,6 +482,13 @@ public class AEMFile extends ADMFile {
this.interpolationDegree = interpolationDegree;
}
/** {@inheritDoc} */
@Override
public int getInterpolationSamples() {
// From the standard it is not entirely clear how to interpret the degree.
return getInterpolationDegree() + 1;
}
/** Get the attitude data lines comment.
* @return the comment
*/
......@@ -467,10 +503,8 @@ public class AEMFile extends ADMFile {
this.attitudeDataLinesComment = new ArrayList<String>(ephemeridesDataLinesComment);
}
/**
* Get the rotation order for Euler angles.
* @return rotation order
*/
/** {@inheritDoc} */
@Override
public RotationOrder getRotationOrder() {
return rotationOrder;
}
......@@ -485,47 +519,5 @@ public class AEMFile extends ADMFile {
}
/** AEM ephemeris blocks for a single satellite. */
public static class AemSatelliteEphemeris {
/** The attitude ephemeris data for the satellite. */
private final List<AttitudeEphemeridesBlock> blocks;
/**
* Create a container for the set of ephemeris blocks in the file that pertain to
* a single satellite.
* @param blocks containing ephemeris data for the satellite.
*/
public AemSatelliteEphemeris(final List<AttitudeEphemeridesBlock> blocks) {
this.blocks = blocks;
}
/**
* Get the segments of the attitude ephemeris.
* <p> Ephemeris segments are typically used to split an attitude ephemeris around
* discontinuous events, such as maneuvers.
* @return the segments contained in the attitude ephemeris file for this satellite.
*/
public List<AttitudeEphemeridesBlock> getSegments() {
return Collections.unmodifiableList(blocks);
}
/**
* Get the start date of the attitude ephemeris.
* @return attitude ephemeris start date.
*/
public AbsoluteDate getStart() {
return blocks.get(0).getStart();
}
/**
* Get the end date of the attitude ephemeris.
* @return attitude ephemeris end date.
*/
public AbsoluteDate getStop() {
return blocks.get(blocks.size() - 1).getStop();
}
}
}
......@@ -33,6 +33,7 @@ import org.orekit.annotation.DefaultDataContext;
import org.orekit.data.DataContext;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.general.AttitudeEphemerisFileParser;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.IERSConventions;
import org.orekit.utils.TimeStampedAngularCoordinates;
......@@ -42,7 +43,7 @@ import org.orekit.utils.TimeStampedAngularCoordinates;
* @author Bryan Cazabonne
* @since 10.2
*/
public class AEMParser extends ADMParser {
public class AEMParser extends ADMParser implements AttitudeEphemerisFileParser {
/** Maximum number of elements in an attitude data line. */
private static final int MAX_SIZE = 8;
......@@ -80,7 +81,7 @@ public class AEMParser extends ADMParser {
* parsing by calling {@link #withInternationalDesignator(int, int, String)}
* </p>
* <p>
* The default interpolation degree is not set here. It is set to zero by default. If another value
* The default interpolation degree is not set here. It is set to one by default. If another value
* is needed it must be initialized before parsing by calling {@link #withInterpolationDegree(int)}
* </p>
*
......@@ -122,7 +123,7 @@ public class AEMParser extends ADMParser {
* parsing by calling {@link #withInternationalDesignator(int, int, String)}
* </p>
* <p>
* The default interpolation degree is not set here. It is set to zero by default. If another value
* The default interpolation degree is not set here. It is set to one by default. If another value
* is needed it must be initialized before parsing by calling {@link #withInterpolationDegree(int)}
* </p>
*
......@@ -131,7 +132,7 @@ public class AEMParser extends ADMParser {
* @see #withDataContext(DataContext)
*/
public AEMParser(final DataContext dataContext) {
this(AbsoluteDate.FUTURE_INFINITY, Double.NaN, null, true, 0, 0, "", 0, dataContext);
this(AbsoluteDate.FUTURE_INFINITY, Double.NaN, null, true, 0, 0, "", 1, dataContext);
}
/**
......@@ -205,7 +206,7 @@ public class AEMParser extends ADMParser {
* <p>
* This method may be used to set a default interpolation degree which will be used
* when no interpolation degree is parsed in the meta-data of the file. Upon instantiation
* with {@link #AEMParser(DataContext)} the default interpolation degree is zero.
* with {@link #AEMParser(DataContext)} the default interpolation degree is one.
* </p>
* @param newInterpolationDegree default interpolation degree to use while parsing
* @return a new instance, with interpolation degree data replaced
......@@ -240,6 +241,7 @@ public class AEMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public AEMFile parse(final InputStream stream, final String fileName) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
return parse(reader, fileName);
......@@ -248,12 +250,8 @@ public class AEMParser extends ADMParser {
}
}
/**
* Parse an attitude ephemeris file from a stream.
* @param reader containing the ephemeris file.
* @param fileName to use in error messages.
* @return a parsed attitude ephemeris file.
*/
/** {@inheritDoc} */
@Override
public AEMFile parse(final BufferedReader reader, final String fileName) {
try {
......
......@@ -27,9 +27,11 @@ import java.util.Map;
import org.orekit.errors.OrekitIllegalArgumentException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.AEMFile.AemSatelliteEphemeris;
import org.orekit.files.ccsds.AEMFile.AttitudeEphemeridesBlock;
import org.orekit.files.ccsds.StreamingAemWriter.AEMSegment;
import org.orekit.files.general.AttitudeEphemerisFile;
import org.orekit.files.general.AttitudeEphemerisFile.SatelliteAttitudeEphemeris;
import org.orekit.files.general.AttitudeEphemerisFile.AttitudeEphemerisSegment;
import org.orekit.files.general.AttitudeEphemerisFileWriter;
import org.orekit.time.TimeScale;
import org.orekit.utils.TimeStampedAngularCoordinates;
......@@ -38,7 +40,7 @@ import org.orekit.utils.TimeStampedAngularCoordinates;
* @author Bryan Cazabonne
* @since 10.2
*/
public class AEMWriter {
public class AEMWriter implements AttitudeEphemerisFileWriter {
/** Originator name, usually the organization and/or country. **/
private final String originator;
......@@ -97,48 +99,41 @@ public class AEMWriter {
this.attitudeFormat = attitudeFormat;
}
/**
* Write the passed in {@link AEMFile} using the passed in {@link Appendable}.
* @param writer a configured Appendable to feed with text
* @param aemFile a populated AEM file to serialize into the buffer
* @throws IOException if any buffer writing operations fail or if the underlying
* format doesn't support a configuration in the EphemerisFile
* for example having multiple satellites in one file, having
* the origin at an unspecified celestial body, etc.)
*/
public void write(final Appendable writer, final AEMFile aemFile)
/** {@inheritDoc} */
@Override
public void write(final Appendable writer, final AttitudeEphemerisFile ephemerisFile)
throws IOException {
if (writer == null) {
throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer");
}
if (aemFile == null) {
if (ephemerisFile == null) {
return;
}
final String idToProcess;
if (spaceObjectId != null) {
if (aemFile.getSatellites().containsKey(spaceObjectId)) {
if (ephemerisFile.getSatellites().containsKey(spaceObjectId)) {
idToProcess = spaceObjectId;
} else {
throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND, spaceObjectId, "ephemerisFile");
}
} else if (aemFile.getSatellites().keySet().size() == 1) {
idToProcess = aemFile.getSatellites().keySet().iterator().next();
} else if (ephemerisFile.getSatellites().keySet().size() == 1) {
idToProcess = ephemerisFile.getSatellites().keySet().iterator().next();
} else {
throw new OrekitIllegalArgumentException(OrekitMessages.EPHEMERIS_FILE_NO_MULTI_SUPPORT);
}
// Get satellite and attitude ephemeris segments to output.
final AemSatelliteEphemeris satEphem = aemFile.getSatellites().get(idToProcess);
final List<AttitudeEphemeridesBlock> segments = satEphem.getSegments();
final SatelliteAttitudeEphemeris satEphem = ephemerisFile.getSatellites().get(idToProcess);
final List<? extends AttitudeEphemerisSegment> segments = satEphem.getSegments();
if (segments.isEmpty()) {
// No data -> No output
return;
}
// First segment
final AttitudeEphemeridesBlock firstSegment = segments.get(0);
final AttitudeEphemerisSegment firstSegment = segments.get(0);
final String objectName = this.spaceObjectName == null ? idToProcess : this.spaceObjectName;
// Only one time scale per AEM file, see Section 4.2.5.4.2
......@@ -152,14 +147,18 @@ public class AEMWriter {
metadata.put(Keyword.OBJECT_ID, idToProcess);
// Header comments. If header comments are presents, they are assembled together in a single line
if (!aemFile.getHeaderComment().isEmpty()) {
// Loop on comments
final StringBuffer buffer = new StringBuffer();
for (String comment : aemFile.getHeaderComment()) {
buffer.append(comment);
if (ephemerisFile instanceof AEMFile) {
// Cast to OEMFile
final AEMFile aemFile = (AEMFile) ephemerisFile;
if (!aemFile.getHeaderComment().isEmpty()) {
// Loop on comments
final StringBuffer buffer = new StringBuffer();
for (String comment : aemFile.getHeaderComment()) {
buffer.append(comment);
}
// Update metadata
metadata.put(Keyword.COMMENT, buffer.toString());
}
// Update metadata
metadata.put(Keyword.COMMENT, buffer.toString());
}
// Writer for AEM files
......@@ -168,7 +167,7 @@ public class AEMWriter {
aemWriter.writeHeader();
// Loop on segments
for (final AttitudeEphemeridesBlock segment : segments) {
for (final AttitudeEphemerisSegment segment : segments) {
// Segment specific metadata
metadata.clear();
metadata.put(Keyword.CENTER_NAME, segment.getFrameCenterString());
......@@ -179,7 +178,8 @@ public class AEMWriter {
metadata.put(Keyword.STOP_TIME, segment.getStop().toString(timeScale));
metadata.put(Keyword.ATTITUDE_TYPE, segment.getAttitudeType());
metadata.put(Keyword.INTERPOLATION_METHOD, segment.getInterpolationMethod());
metadata.put(Keyword.INTERPOLATION_DEGREE, String.valueOf(segment.getInterpolationDegree()));
metadata.put(Keyword.INTERPOLATION_DEGREE,
String.valueOf(segment.getInterpolationSamples() - 1));
final AEMSegment segmentWriter = aemWriter.newSegment(metadata);
segmentWriter.writeMetadata();
......@@ -195,7 +195,20 @@ public class AEMWriter {
}
/**
* Write the passed in {@link AEMFile} to a file at the output path specified.
* Write the passed in {@link AEMFile} using the passed in {@link Appendable}.
* @param writer a configured Appendable to feed with text
* @param aemFile a populated aem file to serialize into the buffer
* @throws IOException if any buffer writing operations fail or if the underlying
* format doesn't support a configuration in the EphemerisFile
* for example having multiple satellites in one file, having
* the origin at an unspecified celestial body, etc.)
*/
public void write(final Appendable writer, final AEMFile aemFile) throws IOException {
write(writer, (AttitudeEphemerisFile) aemFile);
}
/**
* Write the passed in {@link ephemerisFile} to a file at the output path specified.
* @param outputFilePath a file path that the corresponding file will be written to
* @param aemFile a populated aem file to serialize into the buffer
* @throws IOException if any file writing operations fail or if the underlying
......@@ -206,7 +219,7 @@ public class AEMWriter {
public void write(final String outputFilePath, final AEMFile aemFile)
throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputFilePath), StandardCharsets.UTF_8)) {
write(writer, aemFile);
write(writer, (AttitudeEphemerisFile) aemFile);
}
}
......
......@@ -115,6 +115,7 @@ public class APMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public APMParser withMissionReferenceDate(final AbsoluteDate newMissionReferenceDate) {
return new APMParser(newMissionReferenceDate, getMu(), getConventions(), isSimpleEOP(),
getLaunchYear(), getLaunchNumber(), getLaunchPiece(),
......@@ -122,6 +123,7 @@ public class APMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public APMParser withMu(final double newMu) {
return new APMParser(getMissionReferenceDate(), newMu, getConventions(), isSimpleEOP(),
getLaunchYear(), getLaunchNumber(), getLaunchPiece(),
......@@ -129,6 +131,7 @@ public class APMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public APMParser withConventions(final IERSConventions newConventions) {
return new APMParser(getMissionReferenceDate(), getMu(), newConventions, isSimpleEOP(),
getLaunchYear(), getLaunchNumber(), getLaunchPiece(),
......@@ -136,6 +139,7 @@ public class APMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public APMParser withSimpleEOP(final boolean newSimpleEOP) {
return new APMParser(getMissionReferenceDate(), getMu(), getConventions(), newSimpleEOP,
getLaunchYear(), getLaunchNumber(), getLaunchPiece(),
......@@ -143,6 +147,7 @@ public class APMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public APMParser withInternationalDesignator(final int newLaunchYear,
final int newLaunchNumber,
final String newLaunchPiece) {
......@@ -172,6 +177,7 @@ public class APMParser extends ADMParser {
}
/** {@inheritDoc} */
@Override
public APMFile parse(final InputStream stream, final String fileName) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
......