Commit 88052146 authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Added support for SPIN attitude mode in ADM files.

parent ebc05c5a
Pipeline #1060 passed with stage
in 24 minutes and 15 seconds
......@@ -169,6 +169,7 @@ 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(
......
......@@ -48,8 +48,8 @@ public enum AttitudeType {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final TimeStampedAngularCoordinates coordinates) {
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[4];
......@@ -102,8 +102,8 @@ public enum AttitudeType {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final TimeStampedAngularCoordinates coordinates) {
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[8];
......@@ -170,8 +170,8 @@ public enum AttitudeType {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final TimeStampedAngularCoordinates coordinates) {
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[7];
......@@ -230,8 +230,8 @@ public enum AttitudeType {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final TimeStampedAngularCoordinates coordinates) {
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
// Attitude
Rotation rotation = coordinates.getRotation();
......@@ -276,8 +276,8 @@ public enum AttitudeType {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final TimeStampedAngularCoordinates coordinates) {
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final TimeStampedAngularCoordinates coordinates) {
// Initialize the array of attitude data
final double[] data = new double[6];
......@@ -328,19 +328,63 @@ public enum AttitudeType {
},
/** Spin. */
/** 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:
* </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) {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
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.
throw new OrekitException(OrekitMessages.CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED, name());
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, 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));
// 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();
// Convert units and format
return EULER_ANGLE_RATE.formatData(data);
}
/** {@inheritDoc} */
......@@ -351,10 +395,19 @@ public enum AttitudeType {
final boolean isSpacecraftBodyRate,
final Vector3D spinAxis,
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.
throw new OrekitException(OrekitMessages.CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED, name());
// 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);
// Return
return new TimeStampedAngularCoordinates(date, rotation, rotationRate, Vector3D.ZERO);
}
},
......@@ -367,8 +420,8 @@ public enum AttitudeType {
/** {@inheritDoc} */
@Override
public String[] createDataFields(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final TimeStampedAngularCoordinates coordinates) {
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, 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.
......@@ -437,13 +490,14 @@ 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
* (i.e. from inertial frame to spacecraft frame)
* @return the attitude data in CCSDS units
*/
public abstract String[] createDataFields(boolean isFirst, boolean isExternal2SpacecraftBody,
RotationOrder eulerRotSequence, boolean isSpacecraftBodyRate,
TimeStampedAngularCoordinates attitude);
Vector3D spinAxis, TimeStampedAngularCoordinates attitude);
/**
* Get the angular coordinates corresponding to the attitude data.
......@@ -454,7 +508,6 @@ 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 context context binding
* @param fields raw data fields
* @return the angular coordinates, using {@link Attitude Attitude} convention
......@@ -462,8 +515,7 @@ public enum AttitudeType {
*/
public TimeStampedAngularCoordinates parse(final boolean isFirst, final boolean isExternal2SpacecraftBody,
final RotationOrder eulerRotSequence, final boolean isSpacecraftBodyRate,
final Vector3D spinAxis, final ContextBinding context,
final String[] fields) {
final ContextBinding context, final String[] fields) {
// parse the text fields
final AbsoluteDate date = context.getTimeSystem().getConverter(context).parse(fields[0]);
......@@ -474,7 +526,7 @@ public enum AttitudeType {
// build the coordinates
return build(isFirst, isExternal2SpacecraftBody, eulerRotSequence, isSpacecraftBodyRate,
spinAxis, date, components);
context.getSpinAxis(), date, components);
}
......
......@@ -231,7 +231,7 @@ public class AemParser extends AdmParser<AemFile, AemParser> implements Attitude
*/
boolean manageXmlAttitudeStateSection(final boolean starting) {
if (starting) {
currentEntry = new AttitudeEntry(metadata);
currentEntry = new AttitudeEntry(metadata, getSpinAxis());
setFallback(this::processXmlDataToken);
} else {
currentBlock.addData(currentEntry.getCoordinates());
......@@ -305,7 +305,6 @@ public class AemParser extends AdmParser<AemFile, AemParser> implements Attitude
metadata.getEndpoints().isExternal2SpacecraftBody(),
metadata.getEulerRotSeq(),
metadata.isSpacecraftBodyRate(),
null,
context, SPLIT_AT_BLANKS.split(token.getRawContent().trim())));
} catch (NumberFormatException nfe) {
throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
......
......@@ -410,6 +410,7 @@ public class AemWriter extends AbstractMessageWriter<Header, AemSegment, AemFile
metadata.getEndpoints().isExternal2SpacecraftBody(),
metadata.getEulerRotSeq(),
metadata.isSpacecraftBodyRate(),
getContext().getSpinAxis(),
attitude);
if (generator.getFormat() == FileFormat.KVN) {
......@@ -446,9 +447,9 @@ public class AemWriter extends AbstractMessageWriter<Header, AemSegment, AemFile
case EULER_ANGLE_RATE :
writeEulerAngleRate(xmlGenerator, metadata.getEulerRotSeq(), attitude.getDate(), data);
break;
// case SPIN :
// writeSpin(xmlGenerator, attitude.getDate(), data);
// break;
case SPIN :
writeSpin(xmlGenerator, attitude.getDate(), data);
break;
// case SPIN_NUTATION :
// writeSpinNutation(xmlGenerator, attitude.getDate(), data);
// break;
......@@ -648,29 +649,29 @@ public class AemWriter extends AbstractMessageWriter<Header, AemSegment, AemFile
}
// /** Write a spin entry in XML.
// * @param xmlGenerator generator to use for producing output
// * @param epoch of the entry
// * @param data entry data
// * @throws IOException if the output stream throws one while writing.
// */
// void writeSpin(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final String[] data)
// throws IOException {
//
// // wrapping element
// xmlGenerator.enterSection(AttitudeEntryKey.spin.name());
//
// // data part
// xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true);
// int i = 0;
// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ALPHA.name(), data[i++], Unit.DEGREE, true);
// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_DELTA.name(), data[i++], Unit.DEGREE, true);
// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE.name(), data[i++], Unit.DEGREE, true);
// xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE_VEL.name(), data[i++], Units.DEG_PER_S, true);
//
// xmlGenerator.exitSection();
//
// }
/** Write a spin entry in XML.
* @param xmlGenerator generator to use for producing output
* @param epoch of the entry
* @param data entry data
* @throws IOException if the output stream throws one while writing.
*/
void writeSpin(final XmlGenerator xmlGenerator, final AbsoluteDate epoch, final String[] data)
throws IOException {
// wrapping element
xmlGenerator.enterSection(AttitudeEntryKey.spin.name());
// data part
xmlGenerator.writeEntry(AttitudeEntryKey.EPOCH.name(), getTimeConverter(), epoch, true);
int i = 0;
xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ALPHA.name(), data[i++], Unit.DEGREE, true);
xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_DELTA.name(), data[i++], Unit.DEGREE, true);
xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE.name(), data[i++], Unit.DEGREE, true);
xmlGenerator.writeEntry(AttitudeEntryKey.SPIN_ANGLE_VEL.name(), data[i++], Units.DEG_PER_S, true);
xmlGenerator.exitSection();
}
// /** Write a spin/nutation entry in XML.
// * @param xmlGenerator generator to use for producing output
......
......@@ -18,6 +18,7 @@ package org.orekit.files.ccsds.ndm.adm.aem;
import java.util.Arrays;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.files.ccsds.ndm.adm.AttitudeType;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.TimeStampedAngularCoordinates;
......@@ -31,6 +32,9 @@ class AttitudeEntry {
/** Metadata used to interpret the data fields. */
private final AemMetadata metadata;
/** Spin axis in spacecraft body frame. */
private final Vector3D spinAxis;
/** Epoch. */
private AbsoluteDate epoch;
......@@ -39,9 +43,12 @@ class AttitudeEntry {
/** Build an uninitialized entry.
* @param metadata metadata used to interpret the data fields
* @param spinAxis spin axis in spacecraft body frame
* (may be null if attitude type is neither spin nor spin/nutation)
*/
AttitudeEntry(final AemMetadata metadata) {
AttitudeEntry(final AemMetadata metadata, final Vector3D spinAxis) {
this.metadata = metadata;
this.spinAxis = spinAxis;
this.components = new double[8];
Arrays.fill(components, Double.NaN);
}
......@@ -109,10 +116,8 @@ class AttitudeEntry {
public TimeStampedAngularCoordinates getCoordinates() {
return metadata.getAttitudeType().build(metadata.isFirst(),
metadata.getEndpoints().isExternal2SpacecraftBody(),
metadata.getEulerRotSeq(),
metadata.isSpacecraftBodyRate(),
null,
epoch, components);
metadata.getEulerRotSeq(), metadata.isSpacecraftBodyRate(),
spinAxis, epoch, components);
}
}
......@@ -85,8 +85,7 @@ public class ApmFile extends NdmFile<Header, Segment<AdmMetadata, ApmData>> {
final Quaternion q = qBlock.getQuaternion();
final Quaternion qDot = qBlock.getQuaternionDot();
tac = AttitudeType.QUATERNION_DERIVATIVE.build(true, qBlock.getEndpoints().isExternal2SpacecraftBody(),
null, true,
null,
null, true, null,
qBlock.getEpoch(), q.getQ0(), q.getQ1(), q.getQ2(),
q.getQ3(), qDot.getQ0(), qDot.getQ1(), qDot.getQ2(), qDot.getQ3());
} else if (eBlock != null && eBlock.hasRates()) {
......@@ -105,9 +104,7 @@ public class ApmFile extends NdmFile<Header, Segment<AdmMetadata, ApmData>> {
final double[] rates = eBlock.getRotationRates();
tac = AttitudeType.QUATERNION_RATE.build(true,
qBlock.getEndpoints().isExternal2SpacecraftBody(),
eBlock.getEulerRotSeq(),
eBlock.isSpacecraftBodyRate(),
null,
eBlock.getEulerRotSeq(), eBlock.isSpacecraftBodyRate(), null,
qBlock.getEpoch(), q.getQ0(), q.getQ1(), q.getQ2(),
q.getQ3(), rates[0], rates[1], rates[2]);
......@@ -115,8 +112,7 @@ public class ApmFile extends NdmFile<Header, Segment<AdmMetadata, ApmData>> {
// we rely only on the quaternion logical block, despite it doesn't include rates
final Quaternion q = qBlock.getQuaternion();
tac = AttitudeType.QUATERNION.build(true, qBlock.getEndpoints().isExternal2SpacecraftBody(),
null, true,
null,
null, true, null,
qBlock.getEpoch(), q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3());
}
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = ingen rumfartøjsmasse er associeret med denne O
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = ingen IERS-konventioner er angivet før indlæsningen
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = ramme {0} er ikke gyldig i denne CCSDS fils kontekst
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = keine Satellitenmasse definiert in dieser ODM Da
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = es wurden keine IERS Richtlinien eingestellt vor dem Parsen
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = Koordinatennetz {0} ist nicht gültig im Kontext dieser CCSDS Datei
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = δεν υπάρχει μάζα διαστημι
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = δεν υπάρχουν IERs συμβάσεις που έχουν οριστεί πριν από την ανάλυση
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = το πλαίσιο {0} δεν είναι έγκυρο στο πλαίσιο αρχείου CCSDS
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = there is no spacecraft mass associated with this
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = no IERS conventions have been set before parsing
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = no spin axis direction has been set before parsing
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = frame {0} is not valid in this CCSDS file context
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = no hay masa de satélite asociada a este fichero
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = no se ha inicializado ninguna convención IERS antes de la lectura
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = el sistema de referencia {0} no es válido en este contexto de fichero CCSDS
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = il n''y a pas de masse pour le véhicule spatial
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = les conventions IERS n''ont pas été initialisées avant la lecture
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = la direction de l''axe de spin n''a pas été initialisée avant la lecture
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = le repère {0} est invalide dans ce contexte de fichier CCSDS
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = non hai masa de satélite asociada a este fichei
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = non se inicializou ningunha convención IERS antes da lectura
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = o sistema de referencia {0} non é válido neste contexto de ficheiro CCSDS
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = nessuna massa associata al veicolo spaziale nel
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = le convenzioni IERS non sono state inizializzate prima della lettura
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = il sistema di riferimento {0} non è valido in questo contesto del file CCSDS
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = ingen romfartøysmasse er assosiert til denne OD
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = ingen IERS-konvensjoner er blitt satt før lesingen
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = ramme {0} er ikke gyldig i denne CCSDS filens sammenheng
......
......@@ -328,6 +328,9 @@ CCSDS_UNKNOWN_SPACECRAFT_MASS = nu există masa pentru vehiculul spațial din ac
# no IERS conventions have been set before parsing
CCSDS_UNKNOWN_CONVENTIONS = convențiile IERS nu au fost inițializate înainte de citire
# no spin axis direction has been set before parsing
CCSDS_UNKNOWN_SPIN_AXIS = <MISSING TRANSLATION>
# frame {0} is not valid in this CCSDS file context
CCSDS_INVALID_FRAME = sistemul de referință {0} nu este valid în contextul acestui fișier CCSDS
......
......@@ -30,7 +30,7 @@ public class OrekitMessagesTest {
@Test
public void testMessageNumber() {
Assert.assertEquals(242, OrekitMessages.values().length);
Assert.assertEquals(243, OrekitMessages.values().length);
}
@Test
......
......@@ -18,6 +18,7 @@ package org.orekit.files.ccsds.ndm.adm.aem;
import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
import org.hipparchus.geometry.euclidean.threed.FieldRotation;
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;
......@@ -76,36 +77,49 @@ public class AEMAttitudeTypeTest {
context = null;
}
/**
* Attitude type SPIN in CCSDS AEM files is not implemented in Orekit.
* This test verify if an exception is thrown
*/
@Test
public void testSpin() {
// Initialize the attitude type
final AttitudeType spin = AttitudeType.parseType("SPIN");
// Test exception on the first method
try {
spin.parse(true, true, RotationOrder.XYZ, true, null,
context, new String[] { "2021-03-17T00:00:00.000" });
Assert.fail("an exception should have been thrown");
} catch (OrekitException oe) {
Assert.assertEquals(OrekitMessages.CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED, oe.getSpecifier());
Assert.assertEquals(AttitudeType.SPIN.name(), oe.getParts()[0]);
}
// Test computation of angular coordinates from attitude data
final String[] attitudeData = new String[] {
"2021-03-17T00:00:00.000", "-136.19348942398204", "-33.53517498449179", "86.03122596451917", "-14.816053659122998"
};
final TimeStampedAngularCoordinates tsac = spin.parse(true, true, RotationOrder.XYZ, true, context, attitudeData);
final Vector3D spinSat = tsac.getRotationRate().normalize().negate(); // we use negate because SPIN_ANGLE_VEL < 0 here
final Vector3D spinInert = tsac.getRotation().applyInverseTo(spinSat);
Assert.assertEquals(-136.19348942398204, FastMath.toDegrees(spinInert.getAlpha()), ANGLE_PRECISION);
Assert.assertEquals(-33.53517498449179, FastMath.toDegrees(spinInert.getDelta()), ANGLE_PRECISION);
final Vector3D orthogonalInert = spinInert.orthogonal();
final Vector3D orthogonalSat = tsac.getRotation().applyTo(orthogonalInert);
final Vector3D directSat = new Rotation(spinInert, spinSat).applyTo(orthogonalInert);
Assert.assertEquals(86.03122596451917, FastMath.toDegrees(Vector3D.angle(orthogonalSat, directSat)), ANGLE_PRECISION);
Assert.assertEquals(14.816053659122998, FastMath.toDegrees(tsac.getRotationRate().getNorm()), ANGLE_PRECISION);
// Test exception on the second method
try {
spin.createDataFields(true, true, RotationOrder.XYZ, true, null);
Assert.fail("an exception should have been thrown");
} catch (OrekitException oe) {
Assert.assertEquals(OrekitMessages.CCSDS_AEM_ATTITUDE_TYPE_NOT_IMPLEMENTED, oe.getSpecifier());
Assert.assertEquals(AttitudeType.SPIN.name(), oe.getParts()[0]);
// Test computation of attitude data from angular coordinates
AemMetadata metadata = new AemMetadata(3);
metadata.getEndpoints().setFrameA(new FrameFacade(FramesFactory.getGCRF(), CelestialBodyFrame.GCRF,
null, null, "GCRF"));
metadata.getEndpoints().setFrameB(new FrameFacade(null, null, null,
new SpacecraftBodyFrame(SpacecraftBodyFrame.BaseEquipment.GYRO_FRAME, "1"),
"GYRO 1"));
metadata.getEndpoints().setA2b(true);
final String[] attitudeDataBis = spin.createDataFields(metadata.isFirst(),
metadata.getEndpoints().isExternal2SpacecraftBody(),
metadata.getEulerRotSeq(),
metadata.isSpacecraftBodyRate(),
context.getSpinAxis(),
tsac);
for (int i = 0; i < attitudeDataBis.length; i++) {
Assert.assertEquals(Double.parseDouble(attitudeData[i + 1]),
Double.parseDouble(attitudeDataBis[i]),
ANGLE_PRECISION);
}
// no exception on the third method
// Verify angular derivative filter
Assert.assertEquals(AngularDerivativesFilter.USE_RR, spin.getAngularDerivativesFilter());
}
......@@ -122,7 +136,7 @@ public class AEMAttitudeTypeTest {
// Test exception on the first method