Commit 341994d0 authored by Luc Maisonobe's avatar Luc Maisonobe

Added customizable checks for units in parsing.

parent f745d297
Pipeline #1038 failed with stage
in 21 minutes and 23 seconds
......@@ -86,7 +86,7 @@
end
deactivate XyzLexicalAnalyzer
create NDMFile
MessageParser -> NDMFile : new
MessageParser -> NDMFile : build
MessageParser --> Main : NDMFile
deactivate MessageParser
deactivate Main
......
......@@ -109,6 +109,7 @@
class ParserBuilder {
+withConventions()
+withDataContext()
+withParsedUnitsBehavior()
+with...()
+buildOpmParser()
+buildOmmParser()
......@@ -119,6 +120,12 @@
+buildTdmParser()
}
enum ParsedUnitsBehavior {
+IGNORE_PARSED
+CONVERT_COMPATIBLE
+STRICT_COMPLIANCE
}
}
}
......@@ -140,5 +147,6 @@
XmlStructureProcessingState ..|> ProcessingState
ProcessingState <|.. OpmParser
ParserBuilder -right-> OpmParser : build
ParserBuilder o--> ParsedUnitsBehavior
@enduml
/* Copyright 2002-2021 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.ccsds.ndm;
import org.hipparchus.util.Precision;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.utils.units.Unit;
/** Behavior adopted for units that have been parsed from a CCSDS message.
* @author Luc Maisonobe
* @since 11.0
*/
public enum ParsedUnitsBehavior {
/** Ignore parsed units, just relying on CCSDS standard.
* <p>
* When this behavior is selected having a unit parsed as second
* when CCSDS mandates kilometer will be accepted.
* </p>
*/
IGNORE_PARSED {
/** {@inheritDoc} */
@Override
public Unit select(final Unit message, final Unit standard) {
return standard;
}
},
/** Allow compatible units, performing conversion.
* <p>
* When this behavior is selected having a unit parsed as second
* when CCSDS mandates kilometer will be refused, but having a unit
* parsed as meter will be accepted, with proper conversion performed.
* Missing units (i.e. units parsed as {@link Unit#NONE}) are considered
* to be standard.
* </p>
*/
CONVERT_COMPATIBLE {
/** {@inheritDoc} */
@Override
public Unit select(final Unit message, final Unit standard) {
if (message == Unit.NONE) {
return standard;
} else if (message.sameDimension(standard)) {
return message;
} else {
throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS,
message.getName(), standard.getName());
}
}
},
/** Enforce strict compliance with CCSDS standard.
* <p>
* When this behavior is selected having a unit parsed as second
* or as meter when CCSDS mandates kilometer will both be refused.
* Missing units (i.e. units parsed as {@link Unit#NONE}) are considered
* to be standard.
* </p>
*/
STRICT_COMPLIANCE {
/** {@inheritDoc} */
@Override
public Unit select(final Unit message, final Unit standard) {
if (message == Unit.NONE ||
(Precision.equals(message.getScale(), standard.getScale(), 1) &&
message.sameDimension(standard))) {
return standard;
} else {
throw new OrekitException(OrekitMessages.INCOMPATIBLE_UNITS,
message.getName(), standard.getName());
}
}
};
/** Select the unit to use for interpreting parsed value.
* @param message unit parsed in the CCSDS message
* @param standard unit mandated by the standard
* @return selected unit
*/
public abstract Unit select(Unit message, Unit standard);
}
......@@ -54,6 +54,9 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
/** Default interpolation degree. */
private final int defaultInterpolationDegree;
/** Behavior adopted for units that have been parsed from a CCSDS message. */
private final ParsedUnitsBehavior parsedUnitsBehavior;
/**
* Simple constructor.
* <p>
......@@ -66,6 +69,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* <li>{@link #getMu() gravitational coefficient} set to {@code Double.NaN}</li>
* <li>{@link #getDefaultMass() default mass} set to {@code Double.NaN}</li>
* <li>{@link #getDefaultInterpolationDegree() default interpolation degree} set to {@code 1}</li>
* <li>{@link #getParsedUnitsBehavior() parsed unit behavior} set to {@link ParsedUnitsBehavior#CONVERT_COMPATIBLE}</li>
* </ul>
* </p>
*/
......@@ -85,12 +89,14 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* <li>{@link #getMu() gravitational coefficient} set to {@code Double.NaN}</li>
* <li>{@link #getDefaultMass() default mass} set to {@code Double.NaN}</li>
* <li>{@link #getDefaultInterpolationDegree() default interpolation degree} set to {@code 1}</li>
* <li>{@link #getParsedUnitsBehavior() parsed unit behavior} set to {@link ParsedUnitsBehavior#CONVERT_COMPATIBLE}</li>
* </ul>
* </p>
* @param dataContext data context used to retrieve frames, time scales, etc.
*/
public ParserBuilder(final DataContext dataContext) {
this(IERSConventions.IERS_2010, dataContext, null, true, Double.NaN, Double.NaN, 1);
this(IERSConventions.IERS_2010, dataContext, null, true, Double.NaN,
Double.NaN, 1, ParsedUnitsBehavior.CONVERT_COMPATIBLE);
}
/** Complete constructor.
......@@ -101,16 +107,19 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @param mu gravitational coefficient
* @param defaultMass default mass
* @param defaultInterpolationDegree default interpolation degree
* @param parsedUnitsBehavior behavior to adopt for handling parsed units
*/
private ParserBuilder(final IERSConventions conventions, final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final boolean simpleEOP,
final double mu, final double defaultMass,
final int defaultInterpolationDegree) {
final int defaultInterpolationDegree,
final ParsedUnitsBehavior parsedUnitsBehavior) {
super(conventions, dataContext, missionReferenceDate);
this.simpleEOP = simpleEOP;
this.mu = mu;
this.defaultMass = defaultMass;
this.defaultInterpolationDegree = defaultInterpolationDegree;
this.parsedUnitsBehavior = parsedUnitsBehavior;
}
/** {@inheritDoc} */
......@@ -118,7 +127,8 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
protected ParserBuilder create(final IERSConventions newConventions, final DataContext newDataContext,
final AbsoluteDate newMissionReferenceDate) {
return new ParserBuilder(newConventions, newDataContext, newMissionReferenceDate,
simpleEOP, mu, defaultMass, defaultInterpolationDegree);
simpleEOP, mu, defaultMass, defaultInterpolationDegree,
parsedUnitsBehavior);
}
/** Set up flag for ignoring tidal effects when interpolating EOP.
......@@ -127,7 +137,8 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
*/
public ParserBuilder withSimpleEOP(final boolean newSimpleEOP) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
newSimpleEOP, getMu(), getDefaultMass(), getDefaultInterpolationDegree());
newSimpleEOP, getMu(), getDefaultMass(),
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
/** Check if tidal effects are ignored when interpolating EOP.
......@@ -144,7 +155,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
public ParserBuilder withMu(final double newMu) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
isSimpleEOP(), newMu, getDefaultMass(),
getDefaultInterpolationDegree());
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
/** Get the gravitational coefficient.
......@@ -164,7 +175,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
public ParserBuilder withDefaultMass(final double newDefaultMass) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
isSimpleEOP(), getMu(), newDefaultMass,
getDefaultInterpolationDegree());
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
/** Get the default mass.
......@@ -185,7 +196,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
public ParserBuilder withDefaultInterpolationDegree(final int newDefaultInterpolationDegree) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
isSimpleEOP(), getMu(), getDefaultMass(),
newDefaultInterpolationDegree);
newDefaultInterpolationDegree, getParsedUnitsBehavior());
}
/** Get the default interpolation degree.
......@@ -195,12 +206,29 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
return defaultInterpolationDegree;
}
/** Set up the behavior to adopt for handling parsed units.
* @param newParsedUnitsBehavior behavior to adopt for handling parsed units
* @return a new builder with updated configuration (the instance is not changed)
*/
public ParserBuilder withParsedUnitsBehavior(final ParsedUnitsBehavior newParsedUnitsBehavior) {
return new ParserBuilder(getConventions(), getDataContext(), getMissionReferenceDate(),
isSimpleEOP(), getMu(), getDefaultMass(),
getDefaultInterpolationDegree(), newParsedUnitsBehavior);
}
/** Get the behavior to adopt for handling parsed units.
* @return behavior to adopt for handling parsed units
*/
public ParsedUnitsBehavior getParsedUnitsBehavior() {
return parsedUnitsBehavior;
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.opm.OpmFile Orbit Parameters Messages}.
* @return a new parser
*/
public OpmParser buildOpmParser() {
return new OpmParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(),
getMu(), getDefaultMass());
getMu(), getDefaultMass(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.opm.OmmFile Orbit Mean elements Messages}.
......@@ -208,7 +236,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
*/
public OmmParser buildOmmParser() {
return new OmmParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(),
getMu(), getDefaultMass());
getMu(), getDefaultMass(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.oem.OemFile Orbit Ephemeris Messages}.
......@@ -216,21 +244,22 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
*/
public OemParser buildOemParser() {
return new OemParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(),
getMu(), getDefaultInterpolationDegree());
getMu(), getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.odm.ocm.OcmFile Orbit Comprehensive Messages}.
* @return a new parser
*/
public OcmParser buildOcmParser() {
return new OcmParser(getConventions(), isSimpleEOP(), getDataContext(), getMu());
return new OcmParser(getConventions(), isSimpleEOP(), getDataContext(), getMu(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.adm.apm.ApmFile Attitude Parameters Messages}.
* @return a new parser
*/
public ApmParser buildApmParser() {
return new ApmParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate());
return new ApmParser(getConventions(), isSimpleEOP(), getDataContext(),
getMissionReferenceDate(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.adm.aem.AemFile Attitude Ephemeris Messages}.
......@@ -238,7 +267,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
*/
public AemParser buildAemParser() {
return new AemParser(getConventions(), isSimpleEOP(), getDataContext(), getMissionReferenceDate(),
getDefaultInterpolationDegree());
getDefaultInterpolationDegree(), getParsedUnitsBehavior());
}
/** Build a parser for {@link org.orekit.files.ccsds.ndm.tdm.TdmFile Tracking Data Messages}.
......@@ -247,7 +276,7 @@ public class ParserBuilder extends AbstractBuilder<ParserBuilder> {
* @return a new parser
*/
public TdmParser buildTdmParser(final RangeUnitsConverter converter) {
return new TdmParser(getConventions(), isSimpleEOP(), getDataContext(), converter);
return new TdmParser(getConventions(), isSimpleEOP(), getDataContext(), getParsedUnitsBehavior(), converter);
}
}
......@@ -23,6 +23,7 @@ import org.orekit.data.DataContext;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.ndm.NdmFile;
import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
import org.orekit.files.ccsds.utils.lexical.ParseToken;
import org.orekit.files.ccsds.utils.lexical.TokenType;
import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
......@@ -69,11 +70,12 @@ public abstract class AdmParser<T extends NdmFile<?, ?>, P extends AbstractMessa
* @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 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) {
super(root, formatVersionKey, conventions, simpleEOP, dataContext);
protected AdmParser(final String root, final String formatVersionKey, final IERSConventions conventions,
final boolean simpleEOP, final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final ParsedUnitsBehavior parsedUnitsBehavior) {
super(root, formatVersionKey, conventions, simpleEOP, dataContext, parsedUnitsBehavior);
this.missionReferenceDate = missionReferenceDate;
}
......
......@@ -19,6 +19,8 @@ package org.orekit.files.ccsds.ndm.adm;
import org.orekit.files.ccsds.utils.lexical.ParseToken;
import org.orekit.files.ccsds.utils.lexical.TokenType;
import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
import org.orekit.utils.units.Unit;
import org.orekit.utils.units.UnitsCache;
import org.xml.sax.Attributes;
/** Builder for rotation angles and rates.
......@@ -39,6 +41,15 @@ public class RotationXmlTokenBuilder implements XmlTokenBuilder {
/** Attribute name for units. */
private static final String UNITS = "units";
/** Cache for parsed units. */
private final UnitsCache cache;
/** Simple constructor.
*/
public RotationXmlTokenBuilder() {
this.cache = new UnitsCache();
}
/** {@inheritDoc} */
@Override
public ParseToken buildToken(final boolean startTag, final String qName,
......@@ -54,9 +65,11 @@ public class RotationXmlTokenBuilder implements XmlTokenBuilder {
// elaborate the token type
final TokenType type = (content == null) ? (startTag ? TokenType.START : TokenType.STOP) : TokenType.ENTRY;
// get units
final Unit units = cache.getUnits(attributes.getValue(UNITS));
// final build
return new ParseToken(type, name, content, attributes.getValue(UNITS),
lineNumber, fileName);
return new ParseToken(type, name, content, units, lineNumber, fileName);
}
......
......@@ -24,6 +24,7 @@ import org.orekit.data.DataContext;
import org.orekit.data.DataSource;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey;
import org.orekit.files.ccsds.ndm.adm.AdmParser;
import org.orekit.files.ccsds.section.Header;
......@@ -95,11 +96,13 @@ public class AemParser extends AdmParser<AemFile, AemParser> implements Attitude
* @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
* (may be null if time system is absolute)
* @param defaultInterpolationDegree default interpolation degree
* @param parsedUnitsBehavior behavior to adopt for handling parsed units
*/
public AemParser(final IERSConventions conventions, final boolean simpleEOP,
final DataContext dataContext,
final AbsoluteDate missionReferenceDate, final int defaultInterpolationDegree) {
super(AemFile.ROOT, AemFile.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, missionReferenceDate);
final DataContext dataContext, final AbsoluteDate missionReferenceDate,
final int defaultInterpolationDegree, final ParsedUnitsBehavior parsedUnitsBehavior) {
super(AemFile.ROOT, AemFile.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext,
missionReferenceDate, parsedUnitsBehavior);
this.defaultInterpolationDegree = defaultInterpolationDegree;
}
......@@ -127,14 +130,14 @@ public class AemParser extends AdmParser<AemFile, AemParser> implements Attitude
reset(fileFormat, structureProcessor);
} else {
structureProcessor = new KvnStructureProcessingState(this);
reset(fileFormat, new HeaderProcessingState(getDataContext(), this));
reset(fileFormat, new HeaderProcessingState(this));
}
}
/** {@inheritDoc} */
@Override
public boolean prepareHeader() {
setFallback(new HeaderProcessingState(getDataContext(), this));
setFallback(new HeaderProcessingState(this));
return true;
}
......@@ -160,7 +163,8 @@ public class AemParser extends AdmParser<AemFile, AemParser> implements Attitude
}
metadata = new AemMetadata(defaultInterpolationDegree);
context = new ContextBinding(this::getConventions, this::isSimpleEOP,
this::getDataContext, this::getMissionReferenceDate,
this::getDataContext, this::getParsedUnitsBehavior,
this::getMissionReferenceDate,
metadata::getTimeSystem, () -> 0.0, () -> 1.0);
setFallback(this::processMetadataToken);
return true;
......
......@@ -24,6 +24,7 @@ import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.definitions.TimeConverter;
import org.orekit.files.ccsds.definitions.TimeSystem;
import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey;
import org.orekit.files.ccsds.ndm.adm.AttitudeType;
import org.orekit.files.ccsds.section.Header;
......@@ -259,7 +260,8 @@ public class AemWriter extends AbstractMessageWriter<Header, AemSegment, AemFile
final AbsoluteDate missionReferenceDate) {
super(AemFile.ROOT, AemFile.FORMAT_VERSION_KEY, CCSDS_AEM_VERS,
new ContextBinding(
() -> conventions, () -> true, () -> dataContext,
() -> conventions,
() -> true, () -> dataContext, () -> ParsedUnitsBehavior.STRICT_COMPLIANCE,
() -> missionReferenceDate, () -> TimeSystem.UTC,
() -> 0.0, () -> 1.0));
}
......@@ -292,6 +294,7 @@ public class AemWriter extends AbstractMessageWriter<Header, AemSegment, AemFile
setContext(new ContextBinding(oldContext::getConventions,
oldContext::isSimpleEOP,
oldContext::getDataContext,
oldContext::getParsedUnitsBehavior,
oldContext::getReferenceDate,
metadata::getTimeSystem,
oldContext::getClockCount,
......
......@@ -63,56 +63,70 @@ public enum AttitudeEntryKey {
/** Quaternion first vectorial component. */
Q1((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 1 : 0,
Unit.ONE, container::setComponent)),
Unit.ONE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion second vectorial component. */
Q2((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 2 : 1,
Unit.ONE, container::setComponent)),
Unit.ONE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion third vectorial component. */
Q3((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 3 : 2,
Unit.ONE, container::setComponent)),
Unit.ONE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion scalar component. */
QC((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 0 : 3,
Unit.ONE, container::setComponent)),
Unit.ONE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion first vectorial component. */
Q1_DOT((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 1 : 0,
Units.ONE_PER_S, container::setComponent)),
Units.ONE_PER_S, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion second vectorial component. */
Q2_DOT((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 2 : 1,
Units.ONE_PER_S, container::setComponent)),
Units.ONE_PER_S, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion third vectorial component. */
Q3_DOT((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 3 : 2,
Units.ONE_PER_S, container::setComponent)),
Units.ONE_PER_S, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Quaternion scalar component. */
QC_DOT((token, context, container) -> token.processAsIndexedDouble(container.getMetadata().isFirst() ? 0 : 3,
Units.ONE_PER_S, container::setComponent)),
Units.ONE_PER_S, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Rotation about X axis. */
X_ANGLE((token, context, container) -> token.processAsIndexedAngle(0, container::setComponent)),
X_ANGLE((token, context, container) -> token.processAsIndexedDouble(0, Unit.DEGREE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Rotation about Y axis. */
Y_ANGLE((token, context, container) -> token.processAsIndexedAngle(1, container::setComponent)),
Y_ANGLE((token, context, container) -> token.processAsIndexedDouble(1, Unit.DEGREE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Rotation about Z axis. */
Z_ANGLE((token, context, container) -> token.processAsIndexedAngle(2, container::setComponent)),
Z_ANGLE((token, context, container) -> token.processAsIndexedDouble(2, Unit.DEGREE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Rotation about X axis. */
X_RATE((token, context, container) -> token.processAsIndexedAngle(container.firstRotationIndex(),
container::setComponent)),
X_RATE((token, context, container) -> token.processAsIndexedDouble(container.firstRotationIndex(),
Unit.DEGREE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Rotation about Y axis. */
Y_RATE((token, context, container) -> token.processAsIndexedAngle(container.firstRotationIndex() + 1,
container::setComponent)),
Y_RATE((token, context, container) -> token.processAsIndexedDouble(container.firstRotationIndex() + 1,
Unit.DEGREE, context.getParsedUnitsBehavior(),
container::setComponent)),
/** Rotation about Z axis. */
Z_RATE((token, context, container) -> token.processAsIndexedAngle(container.firstRotationIndex() + 2,
container::setComponent));
Z_RATE((token, context, container) -> token.processAsIndexedDouble(container.firstRotationIndex() + 2,
Unit.DEGREE, context.getParsedUnitsBehavior(),
container::setComponent));
/** Processing method. */
private final TokenProcessor processor;
......
......@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import org.orekit.data.DataContext;
import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
import org.orekit.files.ccsds.ndm.adm.AdmMetadata;
import org.orekit.files.ccsds.ndm.adm.AdmMetadataKey;
import org.orekit.files.ccsds.ndm.adm.AdmParser;
......@@ -101,11 +102,12 @@ public class ApmParser extends AdmParser<ApmFile, ApmParser> {
* @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 parsedUnitsBehavior behavior to adopt for handling parsed units
*/