diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 914b115729deb984eac87dc43954b7100fc15c89..31e298b4c04ba3853a93ca818ddcdfe328d3d967 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 0000000000000000000000000000000000000000..b386ccd622b486382b6d55cfc665bb132e2e79cc --- /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 0000000000000000000000000000000000000000..fe637190ef55098e81b2c6d9e4721c3527668b57 --- /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 2dc8edd31162f6c84e13fddf18f1d644a7f0c9f4..e52a5581b075d5855e4b1e09fc5dba97c7f9d808 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 071920a6c156fe31986f75b755089e49fa794e95..2a33b1b4d8b2ebaebc702e511d22ce3d5d649f1c 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 11142ce20ad8a4b990b91018505d0c2ffcabf87d..11f19ef1f11e42726497a9294c8e748d2d28c4e7 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 0000000000000000000000000000000000000000..4f45c7ce63e7bb25a2eb3785b894a7b8c9b582b2 --- /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 e574c7e11c2ea072301623c576b3fab0433ec448..0ff96aeb718363118f0f13103210dba88467503a 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 6e3d5a67c4e23617066e0817a873fc2f532c0ffa..c5d8b07b6b6483c80ba78b61357f581bb6785ee9 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 1e0a91aef342b928c74f6864dfd46e5f0cb162a0..2290555fb358727024b675e4102ca28769e0535e 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 4cae60c0690872f9d06837beb588ae0699359364..2fae9b061756b234491fbe2278a2b6450d8e0247 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 a198855c7ecad145b4813f16d38013499b0c9b1b..bb68314b76b10b6f2f514a02b49987414f07ab5a 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 460ec1b6131b58845c70433329da2fb1065e59f3..06965d346d29dfe459777cae892c9db444b7a470 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 6f15c187415459a19c7b315074283dda8c848c08..fdf9747cf5c30893c46f438bba215ce7d10bf081 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 214c4b53556c03070d13d13c9495d4dc47aa203e..49982b52534f17a5597eaeffab8840002ec9ec0e 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 68066662ad4ea7e753511a37930976071b495a7f..1da0b851efbe1b336b377bd432dcd2d184e52511 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 4bc7529490c1e675ab1b24bdeeb06f6b3364b319..a3cc0b1c2fa3dfe5f0593099f3856173b7e1301f 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 02bf22308d32baa0610b406b88531b16988b79df..7bd95f02366e15d64379e4000f75829ea05818e5 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 0000000000000000000000000000000000000000..e1ba5d509267d155b47306ad21c17374ab9cdf90 --- /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 7a67f50f35c2db452e2ef2b75fb43edb0dc6a2be..824db0bb19764966c9bb520482e6b653609c07cf 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 bbbee4188964b7d9a57c78a757dab7b4a57b5dff..07134f6e47ba796f04cdd828c50698c0a0dd47ff 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 0000000000000000000000000000000000000000..8340f0327727d65ceea47bbbf590d81c747bec14 --- /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