Commit 269b9e20 authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Changed SPIN attitude model according to ADM V2 clarifications.

parent e864a711
......@@ -169,7 +169,6 @@ public enum OrekitMessages implements Localizable {
CCSDS_UNKNOWN_GM("the central body gravitational coefficient cannot be retrieved from the ODM"),
CCSDS_UNKNOWN_SPACECRAFT_MASS("there is no spacecraft mass associated with this ODM file"),
CCSDS_UNKNOWN_CONVENTIONS("no IERS conventions have been set before parsing"),
CCSDS_UNKNOWN_SPIN_AXIS("no spin axis direction has been set before parsing"),
CCSDS_INVALID_FRAME("frame {0} is not valid in this CCSDS file context"),
CCSDS_INCONSISTENT_TIME_SYSTEMS("inconsistent time systems: {0} ≠ {1}"),
CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED(
......
......@@ -16,7 +16,6 @@
*/
package org.orekit.files.ccsds.ndm;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.data.DataContext;
import org.orekit.files.ccsds.ndm.adm.aem.AemParser;
import org.orekit.files.ccsds.ndm.adm.apm.ApmParser;
......@@ -44,9 +43,6 @@ public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
/** Reference date for Mission Elapsed Time or Mission Relative Time time systems. */
private final AbsoluteDate missionReferenceDate;
/** Spin axis in spacecraft body frame. */
private final Vector3D spinAxis;
/** Converter for {@link RangeUnits#RU Range Units}. */
private final RangeUnitsConverter rangeUnitsConverter;
......@@ -55,16 +51,14 @@ public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
* @param conventions IERS Conventions
* @param dataContext used to retrieve frames, time scales, etc.
* @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
* @param spinAxis spin axis in spacecraft body frame
* @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units}
*/
protected AbstractBuilder(final IERSConventions conventions, final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final Vector3D spinAxis,
final AbsoluteDate missionReferenceDate,
final RangeUnitsConverter rangeUnitsConverter) {
this.conventions = conventions;
this.dataContext = dataContext;
this.missionReferenceDate = missionReferenceDate;
this.spinAxis = spinAxis;
this.rangeUnitsConverter = rangeUnitsConverter;
}
......@@ -72,21 +66,18 @@ public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
* @param newConventions IERS Conventions
* @param newDataContext used to retrieve frames, time scales, etc.
* @param newMissionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
* @param newSpinAxis spin axis in spacecraft body frame
* @param newRangeUnitsConverter converter for {@link RangeUnits#RU Range Units}
* @return new instance
*/
protected abstract T create(IERSConventions newConventions, DataContext newDataContext,
AbsoluteDate newMissionReferenceDate, Vector3D newSpinAxis,
RangeUnitsConverter newRangeUnitsConverter);
AbsoluteDate newMissionReferenceDate, RangeUnitsConverter newRangeUnitsConverter);
/** Set up IERS conventions.
* @param newConventions IERS Conventions
* @return a new builder with updated configuration (the instance is not changed)
*/
public T withConventions(final IERSConventions newConventions) {
return create(newConventions, getDataContext(), getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter());
return create(newConventions, getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter());
}
/** Get the IERS conventions.
......@@ -101,8 +92,7 @@ public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public T withDataContext(final DataContext newDataContext) {
return create(getConventions(), newDataContext, getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter());
return create(getConventions(), newDataContext, getMissionReferenceDate(), getRangeUnitsConverter());
}
/** Get the data context.
......@@ -123,8 +113,7 @@ public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public T withMissionReferenceDate(final AbsoluteDate newMissionReferenceDate) {
return create(getConventions(), getDataContext(), newMissionReferenceDate,
getSpinAxis(), getRangeUnitsConverter());
return create(getConventions(), getDataContext(), newMissionReferenceDate, getRangeUnitsConverter());
}
/** Get the mission reference date or Mission Elapsed Time or Mission Relative Time time systems.
......@@ -134,33 +123,12 @@ public abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
return missionReferenceDate;
}
/** Set up spin axis direction.
* <p>
* The spin axis is used only by {@link AemParser} and {@link ApmParser}.
* </p>
* @param newSpinAxis spin axis in spacecraft body frame
* @return a new builder with updated configuration (the instance is not changed)
*/
public T withSpinAxis(final Vector3D newSpinAxis) {
return create(getConventions(), getDataContext(), getMissionReferenceDate(),
newSpinAxis, getRangeUnitsConverter());
}
/**
* Get spin axis in spacecraft body frame.
* @return spin axis
*/
public Vector3D getSpinAxis() {
return spinAxis;
}
/** Set up the converter for {@link RangeUnits#RU Range Units}.
* @param newRangeUnitsConverter converter for {@link RangeUnits#RU Range Units}
* @return a new builder with updated configuration (the instance is not changed)
*/
public T withRangeUnitsConverter(final RangeUnitsConverter newRangeUnitsConverter) {
return create(getConventions(), getDataContext(), getMissionReferenceDate(),
getSpinAxis(), rangeUnitsConverter);
return create(getConventions(), getDataContext(), getMissionReferenceDate(), newRangeUnitsConverter);
}
/** Get the converter for {@link RangeUnits#RU Range Units}.
......
......@@ -16,7 +16,6 @@
*/
package org.orekit.files.ccsds.ndm;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.annotation.DefaultDataContext;
import org.orekit.data.DataContext;
import org.orekit.files.ccsds.ndm.adm.aem.AemParser;
......@@ -99,7 +98,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @param dataContext data context used to retrieve frames, time scales, etc.
*/
public ParserBuilder(final DataContext dataContext) {
this(IERSConventions.IERS_2010, dataContext, null, null, new IdentityConverter(),
this(IERSConventions.IERS_2010, dataContext, null, new IdentityConverter(),
true, Double.NaN, Double.NaN, 1, ParsedUnitsBehavior.CONVERT_COMPATIBLE);
}
......@@ -107,7 +106,6 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @param conventions IERS Conventions
* @param dataContext used to retrieve frames, time scales, etc.
* @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
* @param spinAxis spin axis in spacecraft body frame
* @param simpleEOP if true, tidal effects are ignored when interpolating EOP
* @param mu gravitational coefficient
* @param defaultMass default mass
......@@ -116,12 +114,12 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units}
*/
private ParserBuilder(final IERSConventions conventions, final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final Vector3D spinAxis,
final AbsoluteDate missionReferenceDate,
final RangeUnitsConverter rangeUnitsConverter,
final boolean simpleEOP, final double mu, final double defaultMass,
final int defaultInterpolationDegree,
final ParsedUnitsBehavior parsedUnitsBehavior) {
super(conventions, dataContext, missionReferenceDate, spinAxis, rangeUnitsConverter);
super(conventions, dataContext, missionReferenceDate, rangeUnitsConverter);
this.simpleEOP = simpleEOP;
this.mu = mu;
this.defaultMass = defaultMass;
......@@ -132,9 +130,8 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
/** {@inheritDoc} */
@Override
protected ParserBuilder create(final IERSConventions newConventions, final DataContext newDataContext,
final AbsoluteDate newMissionReferenceDate,
final Vector3D newSpinAxis, final RangeUnitsConverter newRangeUnitsConverter) {
return new ParserBuilder(newConventions, newDataContext, newMissionReferenceDate, newSpinAxis, newRangeUnitsConverter,
final AbsoluteDate newMissionReferenceDate, final RangeUnitsConverter newRangeUnitsConverter) {
return new ParserBuilder(newConventions, newDataContext, newMissionReferenceDate, newRangeUnitsConverter,
simpleEOP, mu, defaultMass, defaultInterpolationDegree, parsedUnitsBehavior);
}
......@@ -143,8 +140,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public ParserBuilder withSimpleEOP(final boolean newSimpleEOP) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter(),
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(),
newSimpleEOP, getMu(), getDefaultMass(),
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
......@@ -161,8 +157,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public ParserBuilder withMu(final double newMu) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter(),
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(),
isSimpleEOP(), newMu, getDefaultMass(),
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
......@@ -182,8 +177,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public ParserBuilder withDefaultMass(final double newDefaultMass) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter(),
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(),
isSimpleEOP(), getMu(), newDefaultMass,
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
......@@ -204,8 +198,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public ParserBuilder withDefaultInterpolationDegree(final int newDefaultInterpolationDegree) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter(),
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(),
isSimpleEOP(), getMu(), getDefaultMass(),
newDefaultInterpolationDegree, getParsedUnitsBehavior());
}
......@@ -222,8 +215,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @return a new builder with updated configuration (the instance is not changed)
*/
public ParserBuilder withParsedUnitsBehavior(final ParsedUnitsBehavior newParsedUnitsBehavior) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
getSpinAxis(), getRangeUnitsConverter(),
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(), getRangeUnitsConverter(),
isSimpleEOP(), getMu(), getDefaultMass(),
getDefaultInterpolationDegree(), newParsedUnitsBehavior);
}
......@@ -278,7 +270,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
*/
public ApmParser buildApmParser() {
return new ApmParser(getConventions(), isSimpleEOP(), getDataContext(),
getMissionReferenceDate(), getSpinAxis(), getParsedUnitsBehavior());
getMissionReferenceDate(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.adm.aem.AemFile Attitude Ephemeris Messages}.
......@@ -286,7 +278,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
*/
public AemParser buildAemParser() {
return new AemParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(),
getDefaultInterpolationDegree(), getSpinAxis(), getParsedUnitsBehavior());
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.tdm.TdmFile Tracking Data Messages}.
......
......@@ -16,7 +16,6 @@
*/
package org.orekit.files.ccsds.ndm;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.annotation.DefaultDataContext;
import org.orekit.data.DataContext;
import org.orekit.files.ccsds.ndm.adm.aem.AemWriter;
......@@ -74,29 +73,25 @@ public class WriterBuilder extends AbstractBuilder<WriterBuilder> {
* @param dataContext data context used to retrieve frames, time scales, etc.
*/
public WriterBuilder(final DataContext dataContext) {
this(IERSConventions.IERS_2010, dataContext, null, null, new IdentityConverter());
this(IERSConventions.IERS_2010, dataContext, null, new IdentityConverter());
}
/** Complete constructor.
* @param conventions IERS Conventions
* @param dataContext used to retrieve frames, time scales, etc.
* @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
* @param spinAxis spin axis in spacecraft body frame
* @param rangeUnitsConverter converter for {@link RangeUnits#RU Range Units}
*/
private WriterBuilder(final IERSConventions conventions, final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final Vector3D spinAxis,
final RangeUnitsConverter rangeUnitsConverter) {
super(conventions, dataContext, missionReferenceDate, spinAxis, rangeUnitsConverter);
final AbsoluteDate missionReferenceDate, final RangeUnitsConverter rangeUnitsConverter) {
super(conventions, dataContext, missionReferenceDate, rangeUnitsConverter);
}
/** {@inheritDoc} */
@Override
protected WriterBuilder create(final IERSConventions newConventions, final DataContext newDataContext,
final AbsoluteDate newMissionReferenceDate,
final Vector3D newSpinAxis, final RangeUnitsConverter newRangeUnitsConverter) {
return new WriterBuilder(newConventions, newDataContext, newMissionReferenceDate,
newSpinAxis, newRangeUnitsConverter);
final AbsoluteDate newMissionReferenceDate, final RangeUnitsConverter newRangeUnitsConverter) {
return new WriterBuilder(newConventions, newDataContext, newMissionReferenceDate, newRangeUnitsConverter);
}
/** Build a writer for {@link org.orekit.files.ccsds.ndm.NdmFile Navigation Data Messages}.
......@@ -138,14 +133,14 @@ public class WriterBuilder extends AbstractBuilder<WriterBuilder> {
* @return a new writer
*/
public ApmWriter buildApmWriter() {
return new ApmWriter(getConventions(), getDataContext(), getMissionReferenceDate(), getSpinAxis());
return new ApmWriter(getConventions(), getDataContext(), getMissionReferenceDate());
}
/** Build a writer for {@link org.orekit.files.ccsds.ndm.adm.aem.AemFile Attitude Ephemeris Messages}.
* @return a new writer
*/
public AemWriter buildAemWriter() {
return new AemWriter(getConventions(), getDataContext(), getMissionReferenceDate(), getSpinAxis());
return new AemWriter(getConventions(), getDataContext(), getMissionReferenceDate());
}
/** Build a writer for {@link org.orekit.files.ccsds.ndm.tdm.TdmFile Tracking Data Messages}.
......
......@@ -19,7 +19,6 @@ package org.orekit.files.ccsds.ndm.adm;
import java.util.Map;
import org.hipparchus.geometry.euclidean.threed.RotationOrder;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.data.DataContext;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
......@@ -63,9 +62,6 @@ public abstract class AdmParser<T extends NdmConstituent<?, ?>, P extends Abstra
/** Reference date for Mission Elapsed Time or Mission Relative Time time systems. */
private final AbsoluteDate missionReferenceDate;
/** Spin axis in spacecraft body frame. */
private final Vector3D spinAxis;
/** Complete constructor.
* @param root root element for XML files
* @param formatVersionKey key for format version
......@@ -74,17 +70,13 @@ public abstract class AdmParser<T extends NdmConstituent<?, ?>, P extends Abstra
* @param dataContext used to retrieve frames, time scales, etc.
* @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
* (may be null if time system is absolute)
* @param spinAxis spin axis in spacecraft body frame
* (may be null if attitude type is neither spin nor spin/nutation)
* @param parsedUnitsBehavior behavior to adopt for handling parsed units
*/
protected AdmParser(final String root, final String formatVersionKey, final IERSConventions conventions,
final boolean simpleEOP, final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final Vector3D spinAxis,
final ParsedUnitsBehavior parsedUnitsBehavior) {
final AbsoluteDate missionReferenceDate, final ParsedUnitsBehavior parsedUnitsBehavior) {
super(root, formatVersionKey, conventions, simpleEOP, dataContext, parsedUnitsBehavior);
this.missionReferenceDate = missionReferenceDate;
this.spinAxis = spinAxis;
}
/** {@inheritDoc} */
......@@ -110,14 +102,6 @@ public abstract class AdmParser<T extends NdmConstituent<?, ?>, P extends Abstra
return missionReferenceDate;
}
/**
* Get spin axis in spacecraft body frame.
* @return spin axis
*/
public Vector3D getSpinAxis() {
return spinAxis;
}
/** Process a CCSDS Euler angles sequence as a {@link RotationOrder}.
* @param sequence Euler angles sequence token
* @param consumer consumer of the rotation order
......
......@@ -24,6 +24,7 @@ import org.hipparchus.geometry.euclidean.threed.Rotation;
import org.hipparchus.geometry.euclidean.threed.RotationConvention;
import org.hipparchus.geometry.euclidean.threed.RotationOrder;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.orekit.attitudes.Attitude;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
......@@ -49,7 +50,8 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[4];
......@@ -77,8 +79,8 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
final double...components) {
Rotation rotation = isFirst ?
new Rotation(components[0], components[1], components[2], components[3], true) :
......@@ -103,7 +105,8 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[8];
......@@ -138,8 +141,8 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
final double...components) {
FieldRotation<UnivariateDerivative1> rotation =
isFirst ?
new FieldRotation<>(new UnivariateDerivative1(components[0], components[4]),
......@@ -171,7 +174,8 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[7];
......@@ -202,8 +206,8 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
final double...components) {
// Build the needed objects
final Rotation rotation = isFirst ?
new Rotation(components[0], components[1], components[2], components[3], true) :
......@@ -231,7 +235,7 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// Attitude
Rotation rotation = coordinates.getRotation();
......@@ -252,8 +256,8 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
final double...components) {
// Build the needed objects
Rotation rotation = new Rotation(eulerRotSequence, RotationConvention.FRAME_TRANSFORM,
......@@ -277,7 +281,8 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[6];
......@@ -305,8 +310,8 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
final double...components) {
// Build the needed objects
final Rotation rotation = new Rotation(eulerRotSequence,
......@@ -330,26 +335,11 @@ public enum AttitudeType {
/** Spin.
* <p>
* CCSDS ADM standard is ambiguous about spin phase angle definition: it does not states what
* are the start and end points of this phase, it only specifies the phase angle is counted
* about the spin axis. This implementation arbitrarily assumes the following:
* CCSDS enforces that spin axis is +Z, so if {@link #createDataFields(boolean, boolean, RotationOrder, boolean,
* TimeStampedAngularCoordinates) createDataFields} is called with {@code coordinates} with {@link
* TimeStampedAngularCoordinates#getRotationRate() rotation rate} that is not along the Z axis, result is
* undefined.
* </p>
* <ul>
* <li>the spin axis is known in spacecraft body frame, and given in an ICD, so it is
* registered in {@link org.orekit.files.ccsds.ndm.ParserBuilder#withSpinAxis(Vector3D)
* ParserBuilder.withSpinAxis(Vector3D)}</li>
* <li>the ADM file defines the spin axis in external frame</li>
* <li>{@code SPIN_DIR} is therefore ignored</li>
* <li>the attitude is the composition of two rotations:
* <ol>
* <li>the rotation with smallest angle that maps spin axis in external frame
* with angular coordinates {@code (SPIN_ALPHA, SPIN_DELTA)} to the spin
* axis in spacecraft frame</li>
* <li>a rotation about spin axis with angle {@code SPIN_ANGLE} and rate
* {@code SPIN_ANGLE_VEL}</li>
* </ol>
* </li>
* </ul>
*/
SPIN("SPIN", AngularDerivativesFilter.USE_RR,
Unit.DEGREE, Unit.DEGREE, Unit.DEGREE, Units.DEG_PER_S) {
......@@ -358,32 +348,22 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[4];
// compute spin as separate direction and rate
// the fixed spinAxis is used only to get the correct sign, the
// pointing direction will really be extracted from the coordinates
final Rotation rotation = coordinates.getRotation();
final Vector3D rate = coordinates.getRotationRate();
final boolean reverse = Vector3D.dotProduct(spinAxis, rate) < 0;
// Attitude
final Vector3D spinSpacecraft = reverse ? rate.negate() : rate;
final Vector3D spinExt = rotation.applyInverseTo(spinSpacecraft);
final Rotation phase = rotation.applyTo(new Rotation(spinSpacecraft, spinExt));
final double[] angles = coordinates.getRotation().getAngles(RotationOrder.ZYZ, RotationConvention.FRAME_TRANSFORM);
// Fill the array
data[0] = spinExt.getAlpha();
data[1] = spinExt.getDelta();
data[2] = Vector3D.dotProduct(spinSpacecraft, phase.getAxis(RotationConvention.FRAME_TRANSFORM)) < 0 ?
-phase.getAngle() : phase.getAngle();
data[3] = reverse ? -rate.getNorm() : rate.getNorm();
data[0] = angles[0];
data[1] = 0.5 * FastMath.PI - angles[1];
data[2] = angles[2];
data[3] = coordinates.getRotationRate().getZ();
// Convert units and format
return EULER_ANGLE_RATE.formatData(data);
return SPIN.formatData(data);
}
......@@ -393,17 +373,16 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
final double...components) {
// Build the needed objects
if (spinAxis == null) {
throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_SPIN_AXIS);
}
final Rotation rotation = new Rotation(spinAxis, components[2], RotationConvention.FRAME_TRANSFORM).
compose(new Rotation(new Vector3D(components[0], components[1]), spinAxis),
RotationConvention.VECTOR_OPERATOR);
final Vector3D rotationRate = new Vector3D(components[3] / spinAxis.getNorm(), spinAxis);
final Rotation rotation = new Rotation(RotationOrder.ZYZ,
RotationConvention.FRAME_TRANSFORM,
components[0],
0.5 * FastMath.PI - components[1],
components[2]);
final Vector3D rotationRate = new Vector3D(0, 0, components[3]);
// Return
return new TimeStampedAngularCoordinates(date, rotation, rotationRate, Vector3D.ZERO);
......@@ -421,7 +400,7 @@ public enum AttitudeType {
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
final TimeStampedAngularCoordinates coordinates) {
// 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.
......@@ -434,8 +413,8 @@ public enum AttitudeType {
final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence,
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
final AbsoluteDate date, final double...components) {
final AbsoluteDate date,
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.
......@@ -490,14 +469,13 @@ public enum AttitudeType {
* @param isExternal2SpacecraftBody true attitude is from external frame to spacecraft body frame
* @param eulerRotSequence sequance of Euler angles
* @param isSpacecraftBodyRate if true Euler rates are specified in spacecraft body frame
* @param spinAxis spin axis in spacecraft body frame
* @param attitude angular coordinates, using {@link Attitude Attitude} convention