From 2ca2847175068121c821400b6a180116c2942e5a Mon Sep 17 00:00:00 2001 From: Bryan Cazabonne Date: Tue, 8 Dec 2020 16:01:24 +0100 Subject: [PATCH] Added aggregator for bounded attitude providers. Fixes #740 --- src/changes/changes.xml | 3 + .../AggregateBoundedAttitudeProvider.java | 129 ++++++++++++ .../attitudes/BoundedAttitudeProvider.java | 42 ++++ .../org/orekit/errors/OrekitMessages.java | 2 + .../java/org/orekit/files/ccsds/AEMFile.java | 15 +- .../files/general/AttitudeEphemerisFile.java | 30 ++- .../EphemerisSegmentAttitudeProvider.java | 108 +++++++++++ .../general/OrekitAttitudeEphemerisFile.java | 15 +- .../localization/OrekitMessages_da.utf8 | 3 + .../localization/OrekitMessages_de.utf8 | 3 + .../localization/OrekitMessages_el.utf8 | 3 + .../localization/OrekitMessages_en.utf8 | 3 + .../localization/OrekitMessages_es.utf8 | 3 + .../localization/OrekitMessages_fr.utf8 | 3 + .../localization/OrekitMessages_gl.utf8 | 3 + .../localization/OrekitMessages_it.utf8 | 3 + .../localization/OrekitMessages_no.utf8 | 3 + .../localization/OrekitMessages_ro.utf8 | 3 + .../AggregateBoundedAttitudeProviderTest.java | 183 ++++++++++++++++++ .../org/orekit/errors/OrekitMessagesTest.java | 2 +- .../org/orekit/files/ccsds/AEMParserTest.java | 12 +- src/test/resources/ccsds/AEMExample10.txt | 55 ++++++ 22 files changed, 598 insertions(+), 28 deletions(-) create mode 100644 src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java create mode 100644 src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java create mode 100644 src/main/java/org/orekit/files/general/EphemerisSegmentAttitudeProvider.java create mode 100644 src/test/java/org/orekit/attitudes/AggregateBoundedAttitudeProviderTest.java create mode 100644 src/test/resources/ccsds/AEMExample10.txt diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 914b11572..31e298b4c 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -21,6 +21,9 @@ + + Added aggregator for bounded attitude providers. + Added Knocke's Earth rediffused radiation pressure force model. diff --git a/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java b/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java new file mode 100644 index 000000000..b386ccd62 --- /dev/null +++ b/src/main/java/org/orekit/attitudes/AggregateBoundedAttitudeProvider.java @@ -0,0 +1,129 @@ +/* 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.attitudes; + +import java.util.Collection; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.TreeMap; + +import org.hipparchus.RealFieldElement; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; + +/** + * A {@link BoundedAttitudeProvider} that covers a larger time span from several constituent + * attitude providers that cover shorter time spans. + * + * @author Bryan Cazabonne + * @since 10.3 + */ +public class AggregateBoundedAttitudeProvider implements BoundedAttitudeProvider { + + /** Constituent attitude provider. */ + private final NavigableMap providers; + + /** + * Constructor. + * @param providers attitude providers that provide the backing data for this instance. + * There must be at least one attitude provider in the collection. + * If there are gaps between the {@link BoundedAttitudeProvider#getMaxDate()} + * of one attitude provider and the {@link BoundedAttitudeProvider#getMinDate()} + * of the next attitude provider an exception may be thrown by any method of + * this class at any time. If there are overlaps between the the {@link + * BoundedAttitudeProvider#getMaxDate()} of one attitude provider and the {@link + * BoundedAttitudeProvider#getMinDate()} of the next attitude provider then the + * attitude provider with the latest {@link BoundedAttitudeProvider#getMinDate()} + * is used. + */ + public AggregateBoundedAttitudeProvider(final Collection providers) { + + // Check if the collection is empty + if (providers.isEmpty()) { + throw new OrekitException(OrekitMessages.NOT_ENOUGH_ATTITUDE_PROVIDERS); + } + + // Initialize map + this.providers = new TreeMap<>(); + + // Loop on providers + for (final BoundedAttitudeProvider provider : providers) { + // Fill collection + this.providers.put(provider.getMinDate(), provider); + } + + } + + /** {@inheritDoc} */ + @Override + public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, + final Frame frame) { + + // Get the attitude provider for the given date + final BoundedAttitudeProvider provider = getAttitudeProvider(date); + + // Build attitude + return provider.getAttitude(pvProv, date, frame); + + } + + /** {@inheritDoc} */ + @Override + public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, final Frame frame) { + + // Get the attitude provider for the given date + final BoundedAttitudeProvider provider = getAttitudeProvider(date.toAbsoluteDate()); + + // Build attitude + return provider.getAttitude(pvProv, date, frame); + + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getMinDate() { + return providers.firstEntry().getValue().getMinDate(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getMaxDate() { + return providers.lastEntry().getValue().getMaxDate(); + } + + /** + * Get the attitude provider to use for the given date. + * @param date of query + * @return attitude provider to use on date. + */ + private BoundedAttitudeProvider getAttitudeProvider(final AbsoluteDate date) { + final Entry attitudeEntry = providers.floorEntry(date); + if (attitudeEntry != null) { + return attitudeEntry.getValue(); + } else { + // Let the first attitude provider throw the exception + return providers.firstEntry().getValue(); + } + } + +} diff --git a/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java b/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java new file mode 100644 index 000000000..fe637190e --- /dev/null +++ b/src/main/java/org/orekit/attitudes/BoundedAttitudeProvider.java @@ -0,0 +1,42 @@ +/* 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.attitudes; + +import org.orekit.time.AbsoluteDate; + +/** This interface is intended for attitude ephemerides valid only during a time range. +* +*

This interface provides a mean to retrieve an attitude at +* any time within a given range. It should be implemented by attitude readers +* based on external data files.

+* +* @author Bryan Cazabonne +* @since 10.3 +*/ +public interface BoundedAttitudeProvider extends AttitudeProvider { + + /** Get the first date of the range. + * @return the first date of the range + */ + AbsoluteDate getMinDate(); + + /** Get the last date of the range. + * @return the last date of the range + */ + AbsoluteDate getMaxDate(); + +} diff --git a/src/main/java/org/orekit/errors/OrekitMessages.java b/src/main/java/org/orekit/errors/OrekitMessages.java index 2dc8edd31..e52a5581b 100644 --- a/src/main/java/org/orekit/errors/OrekitMessages.java +++ b/src/main/java/org/orekit/errors/OrekitMessages.java @@ -220,6 +220,8 @@ public enum OrekitMessages implements Localizable { NOT_ENOUGH_GNSS_FOR_DOP("only {0} GNSS orbits are provided while {1} are needed to compute the DOP"), NOT_ENOUGH_PROPAGATORS( "Creating an aggregate propagator requires at least one constituent propagator, but none were provided."), + NOT_ENOUGH_ATTITUDE_PROVIDERS( + "Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided."), NULL_ARGUMENT("argument {0} cannot be null"), VALUE_NOT_FOUND("value {0} not found in {1}"), EPHEMERIS_FILE_NO_MULTI_SUPPORT("Ephemeris file format does not support multiple space objects"), KLOBUCHAR_ALPHA_BETA_NOT_LOADED("Klobuchar coefficients α or β could not be loaded from {0}"), diff --git a/src/main/java/org/orekit/files/ccsds/AEMFile.java b/src/main/java/org/orekit/files/ccsds/AEMFile.java index 071920a6c..2a33b1b4d 100644 --- a/src/main/java/org/orekit/files/ccsds/AEMFile.java +++ b/src/main/java/org/orekit/files/ccsds/AEMFile.java @@ -24,8 +24,6 @@ import java.util.Map; import java.util.Map.Entry; import org.hipparchus.geometry.euclidean.threed.RotationOrder; -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.TabulatedProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.general.AttitudeEphemerisFile; @@ -313,10 +311,8 @@ public class AEMFile extends ADMFile implements AttitudeEphemerisFile { } - /** - * Get the reference frame from which attitude is defined. - * @return the reference frame from which attitude is defined - */ + /** {@inheritDoc} */ + @Override public Frame getReferenceFrame() { return refFrame; } @@ -560,13 +556,6 @@ public class AEMFile extends ADMFile implements AttitudeEphemerisFile { this.rotationOrder = order; } - /** {@inheritDoc} */ - @Override - public AttitudeProvider getAttitudeProvider() { - return new TabulatedProvider(getReferenceFrame(), getAngularCoordinates(), - getInterpolationSamples(), getAvailableDerivatives()); - } - } diff --git a/src/main/java/org/orekit/files/general/AttitudeEphemerisFile.java b/src/main/java/org/orekit/files/general/AttitudeEphemerisFile.java index 11142ce20..11f19ef1f 100644 --- a/src/main/java/org/orekit/files/general/AttitudeEphemerisFile.java +++ b/src/main/java/org/orekit/files/general/AttitudeEphemerisFile.java @@ -16,11 +16,14 @@ */ package org.orekit.files.general; +import java.util.ArrayList; import java.util.List; import java.util.Map; import org.hipparchus.geometry.euclidean.threed.RotationOrder; -import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.AggregateBoundedAttitudeProvider; +import org.orekit.attitudes.BoundedAttitudeProvider; +import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.utils.AngularDerivativesFilter; @@ -96,6 +99,20 @@ public interface AttitudeEphemerisFile { */ AbsoluteDate getStop(); + /** + * Get the attitude provider corresponding to this ephemeris, combining data from all {@link + * #getSegments() segments}. + * + * @return an attitude provider for all the data in this attitude ephemeris file. + */ + default BoundedAttitudeProvider getAttitudeProvider() { + final List providers = new ArrayList<>(); + for (final AttitudeEphemerisSegment attitudeSegment : this.getSegments()) { + providers.add(attitudeSegment.getAttitudeProvider()); + } + return new AggregateBoundedAttitudeProvider(providers); + } + } /** @@ -141,6 +158,13 @@ public interface AttitudeEphemerisFile { */ String getRefFrameBString(); + /** + * Get the reference frame from which attitude is defined. + * + * @return the reference frame from which attitude is defined + */ + Frame getReferenceFrame(); + /** * Get the rotation direction of the attitude. * @@ -225,7 +249,9 @@ public interface AttitudeEphemerisFile { * * @return the attitude provider for this attitude ephemeris segment. */ - AttitudeProvider getAttitudeProvider(); + default BoundedAttitudeProvider getAttitudeProvider() { + return new EphemerisSegmentAttitudeProvider(this); + } } diff --git a/src/main/java/org/orekit/files/general/EphemerisSegmentAttitudeProvider.java b/src/main/java/org/orekit/files/general/EphemerisSegmentAttitudeProvider.java new file mode 100644 index 000000000..4f45c7ce6 --- /dev/null +++ b/src/main/java/org/orekit/files/general/EphemerisSegmentAttitudeProvider.java @@ -0,0 +1,108 @@ +/* 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.List; +import java.util.stream.Collectors; + +import org.hipparchus.RealFieldElement; +import org.orekit.attitudes.Attitude; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.BoundedAttitudeProvider; +import org.orekit.attitudes.FieldAttitude; +import org.orekit.files.general.AttitudeEphemerisFile.AttitudeEphemerisSegment; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.ImmutableTimeStampedCache; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedFieldAngularCoordinates; + +/** + * An {@link AttitudeProvider} based on an {@link AttitudeEphemerisSegment}. + * @author Bryan Cazabonne + * @since 10.3 + */ +public class EphemerisSegmentAttitudeProvider implements BoundedAttitudeProvider { + + /** Cached attitude table. */ + private final transient ImmutableTimeStampedCache table; + + /** Tabular data from which this attitude provider is built. */ + private final AttitudeEphemerisSegment segment; + + /** + * Constructor. + * @param segment segment containing the attitude data for this provider. + */ + public EphemerisSegmentAttitudeProvider(final AttitudeEphemerisSegment segment) { + this.segment = segment; + this.table = new ImmutableTimeStampedCache(segment.getInterpolationSamples(), + segment.getAngularCoordinates()); + } + + /** {@inheritDoc} */ + @Override + public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, + final Frame frame) { + + // Get attitudes sample on which interpolation will be performed + final List attitudeSample = table.getNeighbors(date).collect(Collectors.toList()); + + // Interpolate attitude data + final TimeStampedAngularCoordinates interpolatedAttitude = + TimeStampedAngularCoordinates.interpolate(date, segment.getAvailableDerivatives(), attitudeSample); + + // Build the interpolated attitude + return new Attitude(segment.getReferenceFrame(), interpolatedAttitude); + + } + + /** {@inheritDoc} */ + @Override + public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, final Frame frame) { + + // Get attitudes sample on which interpolation will be performed + final List> attitudeSample = table.getNeighbors(date.toAbsoluteDate()). + map(ac -> new TimeStampedFieldAngularCoordinates<>(date.getField(), ac)). + collect(Collectors.toList()); + + // Interpolate attitude data + final TimeStampedFieldAngularCoordinates interpolatedAttitude = + TimeStampedFieldAngularCoordinates.interpolate(date, segment.getAvailableDerivatives(), attitudeSample); + + // Build the interpolated attitude + return new FieldAttitude<>(segment.getReferenceFrame(), interpolatedAttitude); + + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getMinDate() { + return segment.getStart(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getMaxDate() { + return segment.getStop(); + } + +} diff --git a/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java b/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java index e574c7e11..0ff96aeb7 100644 --- a/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java +++ b/src/main/java/org/orekit/files/general/OrekitAttitudeEphemerisFile.java @@ -24,8 +24,6 @@ import java.util.concurrent.ConcurrentHashMap; import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.orekit.annotation.DefaultDataContext; -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.TabulatedProvider; import org.orekit.bodies.CelestialBody; import org.orekit.data.DataContext; import org.orekit.errors.OrekitIllegalArgumentException; @@ -518,6 +516,12 @@ public class OrekitAttitudeEphemerisFile implements AttitudeEphemerisFile { return refFrameBString; } + /** {@inheritDoc} */ + @Override + public Frame getReferenceFrame() { + return referenceFrame; + } + /** {@inheritDoc} */ @Override public String getAttitudeDirection() { @@ -584,13 +588,6 @@ public class OrekitAttitudeEphemerisFile implements AttitudeEphemerisFile { return angularDerivativesFilter; } - /** {@inheritDoc} */ - @Override - public AttitudeProvider getAttitudeProvider() { - return new TabulatedProvider(referenceFrame, getAngularCoordinates(), - getInterpolationSamples(), getAvailableDerivatives()); - } - } } diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 index 6e3d5a67c..c5d8b07b6 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = brugen af tidssystemet {0} i CCSDS filer kr # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Oprettelsen af en aggregeret propagator kræver mindst en propagator-bestanddel, men ingen blev angivet. +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = argument {0} kan ikke være null diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 index 1e0a91aef..2290555fb 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = Die Benutzung des Zeitsystems {0} in CCSDS D # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Das erstellen von AggregatePropagator benötigt zumindest einen Bestandteil des Propagator. Es wurden keine zur Verfügung gestellt +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = Das Argument {0} kann nicht "null" sein diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 index 4cae60c06..2fae9b061 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 index a198855c7..bb68314b7 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = use of time system {0} in CCSDS files requir # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Creating an aggregate propagator requires at least one constituent propagator, but none were provided. +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. + # argument {0} cannot be null NULL_ARGUMENT = argument {0} cannot be null diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 index 460ec1b61..06965d346 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = para utilizar el sistema temporal {0} en los # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = para crear un propagador combinado se necesita al menos un propagador, pero no se ha especificado ninguno +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = la entrada {0} no puede ser nula diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 index 6f15c1874..fdf9747cf 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = l''utilisation du système temporel {0} néc # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = la création d''un propagateur combiné nécessite au moins un propagateur, mais aucun n''a été fourni +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = la création d''un fournisseur d''attitude combiné nécessite au moins un fournisseur d''attitude, mais aucun n''a été fourni. + # argument {0} cannot be null NULL_ARGUMENT = l''argument {0} ne devrait pas être nul diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 index 214c4b535..49982b525 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 index 68066662a..1da0b851e 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 @@ -455,6 +455,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = l''utilizzo del sistema temporale {0} nei fi # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = la creazione di un propagatore combinato richiede almeno un propagatore, ma non ne è stato fornito alcuno +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = l''argomento {0} non può essere nullo diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 index 4bc752949..a3cc0b1c2 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = Bruk av tidssystemet {0} i CCSDS-filer kreve # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = En aggregat-propagatør krever minst en bestanddel propagatør, men ingen var spesifisert. +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = argumentet {0} kan ikke være null diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 index 02bf22308..7bd95f023 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 @@ -454,6 +454,9 @@ CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = utilizarea sistemului de timp {0} în fișie # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Crearea unui propagator combinator necesită cel puțin un propagator, dar nici unul nu a fost definit. +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = + # argument {0} cannot be null NULL_ARGUMENT = argumentul {0} nu poate fi nul diff --git a/src/test/java/org/orekit/attitudes/AggregateBoundedAttitudeProviderTest.java b/src/test/java/org/orekit/attitudes/AggregateBoundedAttitudeProviderTest.java new file mode 100644 index 000000000..e1ba5d509 --- /dev/null +++ b/src/test/java/org/orekit/attitudes/AggregateBoundedAttitudeProviderTest.java @@ -0,0 +1,183 @@ +/* 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.attitudes; + +import java.io.InputStream; +import java.util.Collections; + +import org.hipparchus.Field; +import org.hipparchus.RealFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.util.Decimal64Field; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.files.ccsds.AEMFile; +import org.orekit.files.ccsds.AEMFile.AemSatelliteEphemeris; +import org.orekit.files.ccsds.AEMParser; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.IERSConventions; + +public class AggregateBoundedAttitudeProviderTest { + + @Before + public void setUp() { + Utils.setDataRoot("regular-data:ccsds"); + } + + @Test + public void testEmptyList() { + try { + new AggregateBoundedAttitudeProvider(Collections.emptyList()); + } catch (OrekitException oe) { + Assert.assertEquals(OrekitMessages.NOT_ENOUGH_ATTITUDE_PROVIDERS, oe.getSpecifier()); + } + } + + @Test + public void testAEM() { + + final String ex = "/ccsds/AEMExample10.txt"; + final InputStream inEntry = getClass().getResourceAsStream(ex); + final AEMParser parser = new AEMParser().withMu(CelestialBodyFactory.getEarth().getGM()). + withConventions(IERSConventions.IERS_2010). + withSimpleEOP(true); + final AEMFile file = parser.parse(inEntry, "AEMExample10.txt"); + + final AemSatelliteEphemeris ephemeris = file.getSatellites().get("1996-062A"); + final BoundedAttitudeProvider provider = ephemeris.getAttitudeProvider(); + + // Verify dates + Assert.assertEquals(0.0, provider.getMinDate().durationFrom(ephemeris.getStart()), 1.0e-10); + Assert.assertEquals(0.0, provider.getMaxDate().durationFrom(ephemeris.getStop()), 1.0e-10); + Assert.assertEquals(0.0, provider.getMinDate().durationFrom(ephemeris.getSegments().get(0).getStart()), 1.0e-10); + Assert.assertEquals(0.0, provider.getMaxDate().durationFrom(ephemeris.getSegments().get(1).getStop()), 1.0e-10); + + // Verify computation with data in first segment + Attitude attitude = provider.getAttitude(null, new AbsoluteDate("1996-11-28T22:08:04.555", TimeScalesFactory.getUTC()), null); + Rotation rotation = attitude.getRotation(); + Assert.assertEquals(0.45652, rotation.getQ0(), 0.00001); + Assert.assertEquals(-0.84532, rotation.getQ1(), 0.00001); + Assert.assertEquals(0.26974, rotation.getQ2(), 0.00001); + Assert.assertEquals(-0.06532, rotation.getQ3(), 0.00001); + + } + + @Test + public void testFieldAEM() { + doTestFieldAEM(Decimal64Field.getInstance()); + } + + private > void doTestFieldAEM(final Field field) { + + final String ex = "/ccsds/AEMExample10.txt"; + final InputStream inEntry = getClass().getResourceAsStream(ex); + final AEMParser parser = new AEMParser().withMu(CelestialBodyFactory.getEarth().getGM()). + withConventions(IERSConventions.IERS_2010). + withSimpleEOP(true); + final AEMFile file = parser.parse(inEntry, "AEMExample10.txt"); + + final AemSatelliteEphemeris ephemeris = file.getSatellites().get("1996-062A"); + final BoundedAttitudeProvider provider = ephemeris.getAttitudeProvider(); + + // Verify dates + Assert.assertEquals(0.0, provider.getMinDate().durationFrom(ephemeris.getStart()), 1.0e-10); + Assert.assertEquals(0.0, provider.getMaxDate().durationFrom(ephemeris.getStop()), 1.0e-10); + Assert.assertEquals(0.0, provider.getMinDate().durationFrom(ephemeris.getSegments().get(0).getStart()), 1.0e-10); + Assert.assertEquals(0.0, provider.getMaxDate().durationFrom(ephemeris.getSegments().get(1).getStop()), 1.0e-10); + + // Verify computation with data in first segment + FieldAttitude attitude = provider.getAttitude(null, new FieldAbsoluteDate<>(new AbsoluteDate("1996-11-28T22:08:04.555", TimeScalesFactory.getUTC()), field.getZero()), null); + FieldRotation rotation = attitude.getRotation(); + Assert.assertEquals(0.45652, rotation.getQ0().getReal(), 0.00001); + Assert.assertEquals(-0.84532, rotation.getQ1().getReal(), 0.00001); + Assert.assertEquals(0.26974, rotation.getQ2().getReal(), 0.00001); + Assert.assertEquals(-0.06532, rotation.getQ3().getReal(), 0.00001); + + } + + @Test + public void testOutsideBounds() throws Exception { + + final String ex = "/ccsds/AEMExample10.txt"; + final InputStream inEntry = getClass().getResourceAsStream(ex); + final AEMParser parser = new AEMParser().withMu(CelestialBodyFactory.getEarth().getGM()). + withConventions(IERSConventions.IERS_2010). + withSimpleEOP(true); + final AEMFile file = parser.parse(inEntry, "AEMExample10.txt"); + + final AemSatelliteEphemeris ephemeris = file.getSatellites().get("1996-062A"); + final BoundedAttitudeProvider provider = ephemeris.getAttitudeProvider(); + + // before bound of first attitude provider + try { + provider.getAttitude(null, provider.getMinDate().shiftedBy(-60.0), null); + } catch (OrekitException oe) { + Assert.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, oe.getSpecifier()); + } + + // after bound of last attitude provider + try { + provider.getAttitude(null, provider.getMaxDate().shiftedBy(60.0), null); + } catch (OrekitException oe) { + Assert.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, oe.getSpecifier()); + } + + } + + @Test + public void testFieldOutsideBounds() throws Exception { + doTestFieldOutsideBounds(Decimal64Field.getInstance()); + } + + private > void doTestFieldOutsideBounds(final Field field) throws Exception { + + final String ex = "/ccsds/AEMExample10.txt"; + final InputStream inEntry = getClass().getResourceAsStream(ex); + final AEMParser parser = new AEMParser().withMu(CelestialBodyFactory.getEarth().getGM()). + withConventions(IERSConventions.IERS_2010). + withSimpleEOP(true); + final AEMFile file = parser.parse(inEntry, "AEMExample10.txt"); + + final AemSatelliteEphemeris ephemeris = file.getSatellites().get("1996-062A"); + final BoundedAttitudeProvider provider = ephemeris.getAttitudeProvider(); + + // before bound of first attitude provider + try { + provider.getAttitude(null, new FieldAbsoluteDate<>(provider.getMinDate(), field.getZero().subtract(60.0)), null); + } catch (OrekitException oe) { + Assert.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, oe.getSpecifier()); + } + + // after bound of last attitude provider + try { + provider.getAttitude(null, new FieldAbsoluteDate<>(provider.getMinDate(), field.getZero().add(60.0)), null); + } catch (OrekitException oe) { + Assert.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, oe.getSpecifier()); + } + + } + + +} diff --git a/src/test/java/org/orekit/errors/OrekitMessagesTest.java b/src/test/java/org/orekit/errors/OrekitMessagesTest.java index 7a67f50f3..824db0bb1 100644 --- a/src/test/java/org/orekit/errors/OrekitMessagesTest.java +++ b/src/test/java/org/orekit/errors/OrekitMessagesTest.java @@ -30,7 +30,7 @@ public class OrekitMessagesTest { @Test public void testMessageNumber() { - Assert.assertEquals(213, OrekitMessages.values().length); + Assert.assertEquals(214, OrekitMessages.values().length); } @Test diff --git a/src/test/java/org/orekit/files/ccsds/AEMParserTest.java b/src/test/java/org/orekit/files/ccsds/AEMParserTest.java index bbbee4188..07134f6e4 100644 --- a/src/test/java/org/orekit/files/ccsds/AEMParserTest.java +++ b/src/test/java/org/orekit/files/ccsds/AEMParserTest.java @@ -31,7 +31,7 @@ import org.junit.Before; import org.junit.Test; import org.orekit.Utils; import org.orekit.attitudes.Attitude; -import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.BoundedAttitudeProvider; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; @@ -474,13 +474,16 @@ public class AEMParserTest { final AEMFile file = parser.parse(inEntry, "AEMExample8.txt"); Assert.assertEquals(FramesFactory.getEME2000(), file.getAttitudeBlocks().get(0).getReferenceFrame()); - final AttitudeProvider provider = file.getAttitudeBlocks().get(0).getAttitudeProvider(); + final BoundedAttitudeProvider provider = file.getAttitudeBlocks().get(0).getAttitudeProvider(); Attitude attitude = provider.getAttitude(null, new AbsoluteDate("1996-11-28T22:08:03.555", TimeScalesFactory.getUTC()), null); Rotation rotation = attitude.getRotation(); Assert.assertEquals(0.42319, rotation.getQ1(), 0.0001); Assert.assertEquals(-0.45697, rotation.getQ2(), 0.0001); Assert.assertEquals(0.23784, rotation.getQ3(), 0.0001); Assert.assertEquals(0.74533, rotation.getQ0(), 0.0001); + Assert.assertEquals(0.0, provider.getMinDate().durationFrom(file.getAttitudeBlocks().get(0).getStart()), 0.0001); + Assert.assertEquals(0.0, provider.getMaxDate().durationFrom(file.getAttitudeBlocks().get(0).getStop()), 0.0001); + } @Test @@ -494,13 +497,16 @@ public class AEMParserTest { final AEMFile file = parser.parse(inEntry, "AEMExample9.txt"); Assert.assertEquals(FramesFactory.getITRF(ITRFVersion.ITRF_93, IERSConventions.IERS_2010, true), file.getAttitudeBlocks().get(0).getReferenceFrame()); - final AttitudeProvider provider = file.getAttitudeBlocks().get(0).getAttitudeProvider(); + final BoundedAttitudeProvider provider = file.getAttitudeBlocks().get(0).getAttitudeProvider(); Attitude attitude = provider.getAttitude(null, new AbsoluteDate("1996-11-28T22:08:03.555", TimeScalesFactory.getUTC()), null); Rotation rotation = attitude.getRotation(); Assert.assertEquals(0.42319, rotation.getQ1(), 0.0001); Assert.assertEquals(-0.45697, rotation.getQ2(), 0.0001); Assert.assertEquals(0.23784, rotation.getQ3(), 0.0001); Assert.assertEquals(0.74533, rotation.getQ0(), 0.0001); + Assert.assertEquals(0.0, provider.getMinDate().durationFrom(file.getAttitudeBlocks().get(0).getStart()), 0.0001); + Assert.assertEquals(0.0, provider.getMaxDate().durationFrom(file.getAttitudeBlocks().get(0).getStop()), 0.0001); + } } diff --git a/src/test/resources/ccsds/AEMExample10.txt b/src/test/resources/ccsds/AEMExample10.txt new file mode 100644 index 000000000..8340f0327 --- /dev/null +++ b/src/test/resources/ccsds/AEMExample10.txt @@ -0,0 +1,55 @@ +CCSDS_AEM_VERS = 1.0 +CREATION_DATE = 2002-11-04T17:22:31 +ORIGINATOR = NASA/JPL + +META_START +COMMENT This file was produced by M.R. Somebody, MSOO NAV/JPL, 2002 OCT 04. +COMMENT It is to be used for attitude reconstruction only. The relative accuracy of these +COMMENT attitudes is 0.1 degrees per axis. +OBJECT_NAME = MARS GLOBAL SURVEYOR +OBJECT_ID = 1996-062A +CENTER_NAME = MARS BARYCENTER +REF_FRAME_A = EME2000 +REF_FRAME_B = SC_BODY_1 +ATTITUDE_DIR = A2B +TIME_SYSTEM = UTC +START_TIME = 1996-11-28T21:29:07.255 +USEABLE_START_TIME = 1996-11-28T22:08:02.555 +USEABLE_STOP_TIME = 1996-11-30T01:18:02.555 +STOP_TIME = 1996-11-30T01:28:02.555 +ATTITUDE_TYPE = QUATERNION +QUATERNION_TYPE = LAST +INTERPOLATION_METHOD = LINEAR +INTERPOLATION_DEGREE = 1 +META_STOP + +DATA_START +1996-11-28T21:29:07.255 0.56748 0.03146 0.45689 0.68427 +1996-11-28T22:08:03.555 0.42319 -0.45697 0.23784 0.74533 +1996-11-28T22:08:04.555 -0.84532 0.26974 -0.06532 0.45652 +1996-11-30T01:28:02.555 0.74563 -0.45375 0.36875 0.31964 +DATA_STOP + +META_START +COMMENT This block begins after trajectory correction maneuver TCM-3. +OBJECT_NAME = MARS GLOBAL SURVEYOR +OBJECT_ID = 1996-062A +CENTER_NAME = MARS BARYCENTER +REF_FRAME_A = EME2000 +REF_FRAME_B = SC_BODY_1 +ATTITUDE_DIR = A2B +TIME_SYSTEM = UTC +START_TIME = 1996-12-18T12:05:00.555 +USEABLE_START_TIME = 1996-12-18T12:10:00.555 +USEABLE_STOP_TIME = 1996-12-28T21:23:00.555 +STOP_TIME = 1996-12-28T21:28:00.555 +ATTITUDE_TYPE = QUATERNION +QUATERNION_TYPE = LAST +META_STOP + +DATA_START +1996-12-18T12:05:00.555 -0.64585 0.018542 -0.23854 0.72501 +1996-12-18T12:10:05.555 0.87451 -0.43475 0.13458 -0.16767 +1996-12-18T12:10:10.555 0.03125 -0.65874 0.23458 -0.71418 +1996-12-28T21:28:00.555 -0.25485 0.58745 -0.36845 0.67394 +DATA_STOP \ No newline at end of file -- GitLab