OrekitAttitudeEphemerisFile.java
/* Copyright 2002-2020 CS GROUP
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.files.general;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hipparchus.geometry.euclidean.threed.RotationOrder;
import org.orekit.annotation.DefaultDataContext;
import org.orekit.bodies.CelestialBody;
import org.orekit.data.DataContext;
import org.orekit.errors.OrekitIllegalArgumentException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.AEMAttitudeType;
import org.orekit.frames.Frame;
import org.orekit.propagation.SpacecraftState;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScale;
import org.orekit.utils.AngularDerivativesFilter;
import org.orekit.utils.TimeStampedAngularCoordinates;
/**
* A class for encapsulating Orekit propagators within an {@link AttitudeEphemerisFile}
* complaint object that makes for easy serialization to external ephemeris
* formats like AEM.
*
* @author Raphaël Fermé
* @since 10.3
*
*/
public class OrekitAttitudeEphemerisFile implements AttitudeEphemerisFile {
/** Hashmap of satellite ephemeris. **/
private final Map<String, OrekitSatelliteAttitudeEphemeris> satellites;
/**
* Standard default constructor.
*/
public OrekitAttitudeEphemerisFile() {
this.satellites = new ConcurrentHashMap<>();
}
/** {@inheritDoc} */
@Override
public Map<String, OrekitSatelliteAttitudeEphemeris> getSatellites() {
return Collections.unmodifiableMap(satellites);
}
/**
* Adds a new satellite to this object.
*
* @param id
* ID to use for this satellite
* @return the new satellite object
*/
public OrekitSatelliteAttitudeEphemeris addSatellite(final String id) {
final OrekitSatelliteAttitudeEphemeris newSat = new OrekitSatelliteAttitudeEphemeris(id);
this.satellites.put(id, newSat);
return newSat;
}
/**
* Inner class of {@link OrekitAttitudeEphemerisFile} that defines the
* {@link OrekitSatelliteAttitudeEphemeris} corresponding object for this ephemeris type.
*
*/
public static class OrekitSatelliteAttitudeEphemeris implements SatelliteAttitudeEphemeris {
/** Default interpolation sample size if it is not specified. **/
public static final String DEFAULT_INTERPOLATION_METHOD = "LINEAR";
/** Default interpolation sample size if it is not specified. **/
public static final int DEFAULT_INTERPOLATION_SIZE = 2;
/** Default quaternion order if it is not specified. **/
public static final String DEFAULT_ATTITUDE_TYPE = "QUATERNION";
/** Default quaternion order if it is not specified. **/
public static final boolean DEFAULT_IS_FIRST = false;
/** Default rotation order if it is not specified. **/
public static final RotationOrder DEFAULT_ROTATION_ORDER = RotationOrder.ZYX;
/** Default reference frame A name if it is not specified. **/
public static final String DEFAULT_REF_FRAME_A = "EME2000";
/** Default reference frame B name if it is not specified. **/
public static final String DEFAULT_REF_FRAME_B = "SC_BODY_1";
/** Default attitude rotation direction if it is not specified. **/
public static final String DEFAULT_ATTITUDE_DIR = "A2B";
/** ID of the space object encapsulated here. **/
private final String id;
/** Earliest date of this file. **/
private AbsoluteDate startDate;
/** Latest date of this file. **/
private AbsoluteDate stopDate;
/** List of segments in the file. **/
private final List<OrekitAttitudeEphemerisSegment> segments;
/**
* Standard constructor for building the satellite Ephemeris object.
*
* @param id
* the ID of the space object for this data
*/
public OrekitSatelliteAttitudeEphemeris(final String id) {
this.id = id;
this.segments = new ArrayList<OrekitAttitudeEphemerisSegment>();
}
/** {@inheritDoc} */
@Override
public String getId() {
return id;
}
/** {@inheritDoc} */
@Override
public List<? extends AttitudeEphemerisSegment> getSegments() {
return Collections.unmodifiableList(this.segments);
}
/** {@inheritDoc} */
@Override
public AbsoluteDate getStart() {
return this.startDate;
}
/** {@inheritDoc} */
@Override
public AbsoluteDate getStop() {
return this.stopDate;
}
/**
* Injects pre-computed satellite states into this attitude ephemeris file object,
* returning the generated {@link OrekitAttitudeEphemerisSegment} that has been stored
* internally. Defaults the celestial body to earth, time scale to UTC, the interpolation
* size and method, the attitude type, the reference frames to default values.
*
* <p>This method uses the {@link DataContext#getDefault() default data context}.
*
* @param states
* a list of {@link SpacecraftState} that will comprise this
* new unit.
* @return the generated {@link OrekitAttitudeEphemerisSegment}
*/
@DefaultDataContext
public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states) {
return this.addNewSegment(states, DEFAULT_INTERPOLATION_METHOD, DEFAULT_INTERPOLATION_SIZE);
}
/**
* Injects pre-computed satellite states into this attitude ephemeris file object,
* returning the generated {@link OrekitAttitudeEphemerisSegment} that has been stored
* internally. Defaults the celestial body to earth, time scale to UTC, the attitude type,
* the reference frames to default values.
*
* <p>This method uses the {@link DataContext#getDefault() default data context}.
*
* @param states
* a list of {@link SpacecraftState} that will comprise this
* new unit
* @param interpolationMethod
* the interpolation method that should be used when processed
* by another system
* @param interpolationSamples
* the number of interpolation samples that should be used
* when processed by another system
* @return the generated {@link OrekitAttitudeEphemerisSegment}
*/
@DefaultDataContext
public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states,
final String interpolationMethod, final int interpolationSamples) {
return this.addNewSegment(states, interpolationMethod, interpolationSamples, DEFAULT_ATTITUDE_TYPE,
DEFAULT_IS_FIRST, DEFAULT_ROTATION_ORDER);
}
/**
* Injects pre-computed satellite states into this attitude ephemeris file object,
* returning the generated {@link OrekitAttitudeEphemerisSegment} that has been stored
* internally. Defaults the celestial body to earth, time scale to UTC, the reference
* frames to default values.
*
* <p>This method uses the {@link DataContext#getDefault() default data context}.
*
* @param states
* a list of {@link SpacecraftState} that will comprise this
* new unit
* @param interpolationMethod
* the interpolation method that should be used when processed
* by another system
* @param interpolationSamples
* the number of interpolation samples that should be used
* when processed by another system
* @param attitudeType
* type of attitude for the attitude ephemeris segment. Must correspond
* to the names in {@link AEMAttitudeType} enumerate.
* @param isFirst
* flag for placement of the scalar part of the quaternion
* (used if quaternions are chosen in the attitude type)
* @param rotationOrder
* the rotation order for Euler angles (used if Euler angles
* are chosen in the attitude type)
* @return the generated {@link OrekitAttitudeEphemerisSegment}
*/
@DefaultDataContext
public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states, final String interpolationMethod,
final int interpolationSamples, final String attitudeType,
final boolean isFirst, final RotationOrder rotationOrder) {
return this.addNewSegment(states, interpolationMethod, interpolationSamples, attitudeType,
isFirst, rotationOrder, DEFAULT_REF_FRAME_A, DEFAULT_REF_FRAME_B,
DEFAULT_ATTITUDE_DIR);
}
/**
* Injects pre-computed satellite states into this attitude ephemeris file object,
* returning the generated {@link OrekitAttitudeEphemerisSegment} that has been stored
* internally. Defaults the celestial body to earth, time scale to UTC.
*
* <p>This method uses the {@link DataContext#getDefault() default data context}.
*
* @param states
* a list of {@link SpacecraftState} that will comprise this
* new unit
* @param interpolationMethod
* the interpolation method that should be used when processed
* by another system
* @param interpolationSamples
* the number of interpolation samples that should be used
* when processed by another system
* @param attitudeType
* type of attitude for the attitude ephemeris segment. Must correspond
* to the names in {@link AEMAttitudeType} enumerate.
* @param isFirst
* flag for placement of the scalar part of the quaternion
* (used if quaternions are chosen in the attitude type)
* @param rotationOrder
* the rotation order for Euler angles (used if Euler angles
* are chosen in the attitude type)
* @param refFrameA
* name of reference frame A
* @param refFrameB
* name of reference frame B
* @param attitudeDir
* rotation direction of the attitude: "A2B" or "B2A"
* @return the generated {@link OrekitAttitudeEphemerisSegment}
*/
@DefaultDataContext
public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states, final String interpolationMethod,
final int interpolationSamples, final String attitudeType,
final boolean isFirst, final RotationOrder rotationOrder,
final String refFrameA, final String refFrameB,
final String attitudeDir) {
return this.addNewSegment(states, interpolationMethod, interpolationSamples, attitudeType,
isFirst, rotationOrder, refFrameA, refFrameB,
attitudeDir, DataContext.getDefault().getCelestialBodies().getEarth());
}
/**
* Injects pre-computed satellite states into this attitude ephemeris file object,
* returning the generated {@link OrekitAttitudeEphemerisSegment} that has been stored
* internally. Defaults the time scale to UTC.
*
* <p>This method uses the {@link DataContext#getDefault() default data context}.
*
* @param states
* a list of {@link SpacecraftState} that will comprise this
* new unit
* @param interpolationMethod
* the interpolation method that should be used when processed
* by another system
* @param interpolationSamples
* the number of interpolation samples that should be used
* when processed by another system
* @param attitudeType
* type of attitude for the attitude ephemeris segment. Must correspond
* to the names in {@link AEMAttitudeType} enumerate.
* @param isFirst
* flag for placement of the scalar part of the quaternion
* (used if quaternions are chosen in the attitude type)
* @param rotationOrder
* the rotation order for Euler angles (used if Euler angles
* are chosen in the attitude type)
* @param refFrameA
* name of reference frame A
* @param refFrameB
* name of reference frame B
* @param attitudeDir
* rotation direction of the attitude: "A2B" or "B2A"
* @param body
* the celestial body from which the frames are defined
* @return the generated {@link OrekitAttitudeEphemerisSegment}
*/
@DefaultDataContext
public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states, final String interpolationMethod,
final int interpolationSamples, final String attitudeType,
final boolean isFirst, final RotationOrder rotationOrder,
final String refFrameA, final String refFrameB,
final String attitudeDir, final CelestialBody body) {
return this.addNewSegment(states, interpolationMethod, interpolationSamples, attitudeType,
isFirst, rotationOrder, refFrameA, refFrameB, attitudeDir, body,
DataContext.getDefault().getTimeScales().getUTC());
}
/**
* Injects pre-computed satellite states into this attitude ephemeris file
* object, returning the generated {@link OrekitAttitudeEphemerisSegment} that
* has been stored internally.
*
* @param states
* a list of {@link SpacecraftState} that will comprise this
* new unit
* @param body
* the celestial body from which the frames are defined
* @param refFrameA
* name of reference frame A
* @param refFrameB
* name of reference frame B
* @param attitudeDir
* rotation direction of the attitude: "A2B" or "B2A"
* @param attitudeType
* type of attitude for the attitude ephemeris segment.
* @param isFirst
* flag for placement of the scalar part of the quaternion
* (used if quaternions are chosen in the attitude type)
* @param rotationOrder
* the rotation order for Euler angles (used if Euler angles
* are chosen in the attitude type)
* @param timeScale
* the time scale used in the new segment.
* @param interpolationMethod
* the interpolation method that should be used when processed
* by another system
* @param interpolationSamples
* the number of interpolation samples that should be used
* when processed by another system
* @return the generated {@link OrekitAttitudeEphemerisSegment}
*/
public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states, final String interpolationMethod,
final int interpolationSamples, final String attitudeType,
final boolean isFirst, final RotationOrder rotationOrder,
final String refFrameA, final String refFrameB,
final String attitudeDir, final CelestialBody body,
final TimeScale timeScale) {
final int minimumSampleSize = 2;
if (states == null || states.size() == 0) {
throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "states");
}
if (interpolationSamples < minimumSampleSize) {
throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION,
interpolationSamples);
}
final AbsoluteDate start = states.get(0).getDate();
final AbsoluteDate stop = states.get(states.size() - 1).getDate();
if (this.startDate == null || start.compareTo(this.startDate) < 0) {
this.startDate = start;
}
if (this.stopDate == null || stop.compareTo(this.stopDate) > 0) {
this.stopDate = stop;
}
final List<TimeStampedAngularCoordinates> attitudeDataLines = new ArrayList<TimeStampedAngularCoordinates>();
for (SpacecraftState state : states) {
attitudeDataLines.add(state.getAttitude().getOrientation());
}
final OrekitAttitudeEphemerisSegment newSeg = new OrekitAttitudeEphemerisSegment(attitudeDataLines, body.getName(), refFrameA,
refFrameB, attitudeDir, attitudeType, isFirst, rotationOrder,
timeScale, interpolationMethod, interpolationSamples, states.get(0).getFrame());
this.segments.add(newSeg);
return newSeg;
}
}
public static class OrekitAttitudeEphemerisSegment implements AttitudeEphemerisSegment {
/** List of attitude data lines. */
private List<TimeStampedAngularCoordinates> attitudeDataLines;
/** The name of the frame center. **/
private final String frameCenterString;
/** The reference frame A specifier, as it appeared in the file. */
private String refFrameAString;
/** The reference frame B specifier, as it appeared in the file. */
private String refFrameBString;
/** Rotation direction of the attitude. */
private String attitudeDir;
/** The format of the data lines in the message. */
private String attitudeType;
/** The placement of the scalar portion of the quaternion (QC) in the attitude data. */
private boolean isFirst;
/** The rotation order. */
private RotationOrder rotationOrder;
/** The time scale identifier, as specified in the ephemeris file. **/
private final String timeScaleString;
/** The time scale for this segment. **/
private final TimeScale timeScale;
/** The interpolation method to be used. */
private String interpolationMethod;
/** The number of interpolation samples. */
private int interpolationSamples;
/** Enumerate for selecting which derivatives to use in {@link #attitudeDataLines} interpolation. */
private AngularDerivativesFilter angularDerivativesFilter;
/** Reference frame from which attitude is defined. */
private Frame referenceFrame;
/**
* Constructor for OrekitAttitudeEphemerisSegment.
*
* @param attitudeDataLines
* attitude data lines for this segment.
* @param frameCenterString
* the name of celestial body the frame is attached to.
* @param refFrameAString
* the name of the reference frame A.
* @param refFrameBString
* the name of the reference frame B.
* @param attitudeDir
* the rotation direction of the attitude.
* @param attitudeType
* the format of the attitude data.
* @param isFirst
* true if QC is the first element in the attitude data.
* @param rotationOrder
* the rotation order for Euler angles.
* @param timeScale
* the time scale of these ephemerides data.
* @param interpolationMethod
* the interpolation method to use.
* @param interpolationSamples
* the number of samples to use during interpolation.
* @param referenceFrame
* reference frame from which the attitude is defined
*/
public OrekitAttitudeEphemerisSegment(final List<TimeStampedAngularCoordinates> attitudeDataLines, final String frameCenterString,
final String refFrameAString, final String refFrameBString, final String attitudeDir, final String attitudeType,
final boolean isFirst, final RotationOrder rotationOrder, final TimeScale timeScale,
final String interpolationMethod, final int interpolationSamples,
final Frame referenceFrame) {
this.attitudeDataLines = attitudeDataLines;
this.frameCenterString = frameCenterString;
this.refFrameAString = refFrameAString;
this.refFrameBString = refFrameBString;
this.attitudeDir = attitudeDir;
this.attitudeType = attitudeType;
this.isFirst = isFirst;
this.rotationOrder = rotationOrder;
this.timeScaleString = timeScale.getName();
this.timeScale = timeScale;
this.interpolationMethod = interpolationMethod;
this.interpolationSamples = interpolationSamples;
this.referenceFrame = referenceFrame;
this.angularDerivativesFilter = AEMAttitudeType.getAttitudeType(attitudeType).getAngularDerivativesFilter();
}
/** {@inheritDoc} */
@Override
public List<TimeStampedAngularCoordinates> getAngularCoordinates() {
return Collections.unmodifiableList(attitudeDataLines);
}
/** {@inheritDoc} */
@Override
public String getFrameCenterString() {
return frameCenterString;
}
/** {@inheritDoc} */
@Override
public String getRefFrameAString() {
return refFrameAString;
}
/** {@inheritDoc} */
@Override
public String getRefFrameBString() {
return refFrameBString;
}
/** {@inheritDoc} */
@Override
public Frame getReferenceFrame() {
return referenceFrame;
}
/** {@inheritDoc} */
@Override
public String getAttitudeDirection() {
return attitudeDir;
}
/** {@inheritDoc} */
@Override
public String getAttitudeType() {
return attitudeType;
}
/** {@inheritDoc} */
@Override
public boolean isFirst() {
return isFirst;
}
/** {@inheritDoc} */
@Override
public RotationOrder getRotationOrder() {
return rotationOrder;
}
/** {@inheritDoc} */
@Override
public String getTimeScaleString() {
return timeScaleString;
}
/** {@inheritDoc} */
@Override
public TimeScale getTimeScale() {
return timeScale;
}
/** {@inheritDoc} */
@Override
public AbsoluteDate getStart() {
return attitudeDataLines.get(0).getDate();
}
/** {@inheritDoc} */
@Override
public AbsoluteDate getStop() {
return attitudeDataLines.get(attitudeDataLines.size() - 1).getDate();
}
/** {@inheritDoc} */
@Override
public String getInterpolationMethod() {
return interpolationMethod;
}
/** {@inheritDoc} */
@Override
public int getInterpolationSamples() {
return interpolationSamples;
}
/** {@inheritDoc} */
@Override
public AngularDerivativesFilter getAvailableDerivatives() {
return angularDerivativesFilter;
}
}
}