1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.attitudes;
18  
19  import java.util.Collection;
20  import java.util.Map.Entry;
21  import java.util.ArrayList;
22  import java.util.NavigableMap;
23  import java.util.List;
24  import java.util.TreeMap;
25  import java.util.stream.Stream;
26  
27  import org.hipparchus.CalculusFieldElement;
28  import org.hipparchus.Field;
29  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
30  import org.hipparchus.geometry.euclidean.threed.Rotation;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitMessages;
33  import org.orekit.frames.Frame;
34  import org.orekit.propagation.events.DateDetector;
35  import org.orekit.propagation.events.EventDetector;
36  import org.orekit.propagation.events.FieldDateDetector;
37  import org.orekit.propagation.events.FieldEventDetector;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.FieldAbsoluteDate;
40  import org.orekit.utils.FieldPVCoordinatesProvider;
41  import org.orekit.utils.PVCoordinatesProvider;
42  import org.orekit.utils.ParameterDriver;
43  
44  /**
45   * A {@link BoundedAttitudeProvider} that covers a larger time span from several constituent
46   * attitude providers that cover shorter time spans.
47   *
48   * @author Bryan Cazabonne
49   * @since 10.3
50   */
51  public class AggregateBoundedAttitudeProvider implements BoundedAttitudeProvider {
52  
53      /** Constituent attitude provider. */
54      private final NavigableMap<AbsoluteDate, BoundedAttitudeProvider> providers;
55  
56      /**
57       * Constructor.
58       * @param providers attitude providers that provide the backing data for this instance.
59       *                  There must be at least one attitude provider in the collection.
60       *                  If there are gaps between the {@link BoundedAttitudeProvider#getMaxDate()}
61       *                  of one attitude provider and the {@link BoundedAttitudeProvider#getMinDate()}
62       *                  of the next attitude provider an exception may be thrown by any method of
63       *                  this class at any time. If there are overlaps between the the {@link
64       *                  BoundedAttitudeProvider#getMaxDate()} of one attitude provider and the {@link
65       *                  BoundedAttitudeProvider#getMinDate()} of the next attitude provider then the
66       *                  attitude provider with the latest {@link BoundedAttitudeProvider#getMinDate()}
67       *                  is used.
68       */
69      public AggregateBoundedAttitudeProvider(final Collection<? extends BoundedAttitudeProvider> providers) {
70  
71          // Check if the collection is empty
72          if (providers.isEmpty()) {
73              throw new OrekitException(OrekitMessages.NOT_ENOUGH_ATTITUDE_PROVIDERS);
74          }
75  
76          // Initialize map
77          this.providers = new TreeMap<>();
78  
79          // Loop on providers
80          for (final BoundedAttitudeProvider provider : providers) {
81              // Fill collection
82              this.providers.put(provider.getMinDate(), provider);
83          }
84  
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date,
90                                  final Frame frame) {
91  
92          // Get the attitude provider for the given date
93          final BoundedAttitudeProvider provider = getAttitudeProvider(date);
94  
95          // Build attitude
96          return provider.getAttitude(pvProv, date, frame);
97  
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
103                                                                             final FieldAbsoluteDate<T> date,
104                                                                             final Frame frame) {
105 
106         // Get the attitude provider for the given date
107         final BoundedAttitudeProvider provider = getAttitudeProvider(date.toAbsoluteDate());
108 
109         // Build attitude
110         return provider.getAttitude(pvProv, date, frame);
111 
112     }
113 
114     /** {@inheritDoc} */
115     @Override
116     public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
117         return getAttitudeProvider(date).getAttitudeRotation(pvProv, date, frame);
118     }
119 
120     /** {@inheritDoc} */
121     @Override
122     public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
123                                                                                     final FieldAbsoluteDate<T> date,
124                                                                                     final Frame frame) {
125         return getAttitudeProvider(date.toAbsoluteDate()).getAttitudeRotation(pvProv, date, frame);
126     }
127 
128     @Override
129     public AbsoluteDate getMinDate() {
130         return providers.firstEntry().getValue().getMinDate();
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public AbsoluteDate getMaxDate() {
136         return providers.lastEntry().getValue().getMaxDate();
137     }
138 
139     /**
140      * Get the attitude provider to use for the given date.
141      * @param date of query
142      * @return attitude provider to use on date.
143      */
144     private BoundedAttitudeProvider getAttitudeProvider(final AbsoluteDate date) {
145         final Entry<AbsoluteDate, BoundedAttitudeProvider> attitudeEntry = providers.floorEntry(date);
146         if (attitudeEntry != null) {
147             return attitudeEntry.getValue();
148         } else {
149             // Let the first attitude provider throw the exception
150             return providers.firstEntry().getValue();
151         }
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public Stream<EventDetector> getEventDetectors() {
157         final List<AbsoluteDate> dates = new ArrayList<>(providers.navigableKeySet());
158         final DateDetector detector = getDateDetector(dates.toArray(new AbsoluteDate[0]));
159         return Stream.concat(Stream.of(detector), getEventDetectors(getParametersDrivers()));
160     }
161 
162     /** {@inheritDoc} */
163     @Override
164     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
165         final List<AbsoluteDate> dates = new ArrayList<>(providers.navigableKeySet());
166         final FieldDateDetector<T> detector = getFieldDateDetector(field, dates.toArray(new AbsoluteDate[0]));
167         return Stream.concat(Stream.of(detector), getFieldEventDetectors(field, getParametersDrivers()));
168     }
169 
170     /** {@inheritDoc} */
171     @Override
172     public List<ParameterDriver> getParametersDrivers() {
173         final List<ParameterDriver> drivers = new ArrayList<>();
174         providers.values().forEach(provider -> drivers.addAll(provider.getParametersDrivers()));
175         return drivers;
176     }
177 }