1 /* Copyright 2002-2025 Joseph Reed
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 * Joseph Reed 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.utils;
18
19 import java.util.Objects;
20
21 import org.hipparchus.geometry.euclidean.threed.Vector3D;
22 import org.orekit.errors.OrekitException;
23 import org.orekit.errors.OrekitMessages;
24 import org.orekit.frames.Frame;
25 import org.orekit.time.AbsoluteDate;
26
27 /** Aggregate multiple {@link PVCoordinatesProvider} instances together.
28 * <p>
29 * This can be used to describe an aircraft or surface vehicle.
30 * </p>
31 * @author Joe Reed
32 * @since 11.3
33 */
34 public class AggregatedPVCoordinatesProvider implements PVCoordinatesProvider {
35
36 /** Map of provider instances by transition time. */
37 private final TimeSpanMap<PVCoordinatesProvider> pvProvMap;
38
39 /** Earliest date at which {@link #getPVCoordinates(AbsoluteDate, Frame)} will return a valid result. */
40 private final AbsoluteDate minDate;
41
42 /** Latest date at which {@link #getPVCoordinates(AbsoluteDate, Frame)} will return a valid result. */
43 private final AbsoluteDate maxDate;
44
45 /** Class constructor.
46 * <p>
47 * Note the provided {@code map} is used directly. Modification of the
48 * map after calling this constructor may result in undefined behavior.
49 * </p>
50 * @param map the map of {@link PVCoordinatesProvider} instances by time.
51 */
52 public AggregatedPVCoordinatesProvider(final TimeSpanMap<PVCoordinatesProvider> map) {
53 this(map, null, null);
54 }
55
56 /** Class constructor.
57 * <p>
58 * Note the provided {@code map} is used directly. Modification of the
59 * map after calling this constructor may result in undefined behavior.
60 * </p>
61 * @param map the map of {@link PVCoordinatesProvider} instances by time.
62 * @param minDate the earliest valid date, {@code null} if always valid
63 * @param maxDate the latest valid date, {@code null} if always valid
64 */
65 public AggregatedPVCoordinatesProvider(final TimeSpanMap<PVCoordinatesProvider> map,
66 final AbsoluteDate minDate, final AbsoluteDate maxDate) {
67 this.pvProvMap = Objects.requireNonNull(map, "PVCoordinatesProvider map must be non-null");
68 this.minDate = minDate == null ? AbsoluteDate.PAST_INFINITY : minDate;
69 this.maxDate = maxDate == null ? AbsoluteDate.FUTURE_INFINITY : maxDate;
70 }
71
72 /** Get the first date of the range.
73 * @return the first date of the range
74 */
75 public AbsoluteDate getMinDate() {
76 return minDate;
77 }
78
79 /** Get the last date of the range.
80 * @return the last date of the range
81 */
82 public AbsoluteDate getMaxDate() {
83 return maxDate;
84 }
85
86 @Override
87 public Vector3D getPosition(final AbsoluteDate date, final Frame frame) {
88 if (date.isBefore(minDate) || date.isAfter(maxDate)) {
89 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate);
90 }
91 return pvProvMap.get(date).getPosition(date, frame);
92 }
93
94 @Override
95 public Vector3D getVelocity(final AbsoluteDate date, final Frame frame) {
96 if (date.isBefore(minDate) || date.isAfter(maxDate)) {
97 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate);
98 }
99 return pvProvMap.get(date).getVelocity(date, frame);
100 }
101
102 @Override
103 public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
104 if (date.isBefore(minDate) || date.isAfter(maxDate)) {
105 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate);
106 }
107 return pvProvMap.get(date).getPVCoordinates(date, frame);
108 }
109
110 /**
111 * Builder class for {@link AggregatedPVCoordinatesProvider}.
112 */
113 public static class Builder {
114
115 /** Time span map holding the incremental values. */
116 private final TimeSpanMap<PVCoordinatesProvider> pvProvMap;
117
118 /**
119 * Create a builder using the {@link InvalidPVProvider} as the initial provider.
120 */
121 public Builder() {
122 this(new InvalidPVProvider());
123 }
124
125 /**
126 * Create a builder using the provided initial provider.
127 *
128 * @param initialProvider the inital provider
129 */
130 public Builder(final PVCoordinatesProvider initialProvider) {
131 pvProvMap = new TimeSpanMap<>(initialProvider);
132 }
133
134 /** Add a {@link PVCoordinatesProvider} to the collection.
135 * <p>
136 * The provided date is the transition time, at which this provider will be used.
137 * </p>
138 * @param date the transition date
139 * @param pvProv the provider
140 * @param erasesLater if true, the entry erases all existing transitions that are later than {@code date}
141 * @return this builder instance
142 * @see TimeSpanMap#addValidAfter(Object, AbsoluteDate, boolean)
143 */
144 public Builder addPVProviderAfter(final AbsoluteDate date,
145 final PVCoordinatesProvider pvProv,
146 final boolean erasesLater) {
147 pvProvMap.addValidAfter(pvProv, date, erasesLater);
148 return this;
149 }
150
151 /** Add a {@link PVCoordinatesProvider} to the collection.
152 * <p>
153 * The provided date is the final transition time, before which this provider will be used.
154 * </p>
155 * @param date the transition date
156 * @param pvProv the provider
157 * @param erasesEarlier if true, the entry erases all existing transitions that are earlier than {@code date}
158 * @return this builder instance
159 * @see TimeSpanMap#addValidBefore(Object, AbsoluteDate, boolean)
160 */
161 public Builder addPVProviderBefore(final AbsoluteDate date, final PVCoordinatesProvider pvProv, final boolean erasesEarlier) {
162 pvProvMap.addValidBefore(pvProv, date, erasesEarlier);
163 return this;
164 }
165
166 /** Indicate the date before which the resulting PVCoordinatesProvider is invalid.
167 *
168 * @param firstValidDate first date at which the resuling provider should be valid
169 * @return this instance
170 */
171 public Builder invalidBefore(final AbsoluteDate firstValidDate) {
172 pvProvMap.addValidBefore(new InvalidPVProvider(), firstValidDate, true);
173 return this;
174 }
175
176 /** Indicate the date after which the resulting PVCoordinatesProvider is invalid.
177 *
178 * @param lastValidDate last date at which the resuling provider should be valid
179 * @return this instance
180 */
181 public Builder invalidAfter(final AbsoluteDate lastValidDate) {
182 pvProvMap.addValidAfter(new InvalidPVProvider(), lastValidDate, true);
183 return this;
184 }
185
186 /** Build the aggregated PVCoordinatesProvider.
187 *
188 * @return the new provider instance.
189 */
190 public AggregatedPVCoordinatesProvider build() {
191 AbsoluteDate minDate = null;
192 AbsoluteDate maxDate = null;
193 // check the first span
194 if (pvProvMap.getFirstTransition() != null) {
195 if (pvProvMap.getFirstTransition().getBefore() instanceof InvalidPVProvider) {
196 minDate = pvProvMap.getFirstTransition().getDate();
197 }
198 }
199 if (pvProvMap.getLastTransition() != null) {
200 if (pvProvMap.getLastTransition().getAfter() instanceof InvalidPVProvider) {
201 maxDate = pvProvMap.getLastTransition().getDate();
202 }
203 }
204 return new AggregatedPVCoordinatesProvider(pvProvMap, minDate, maxDate);
205 }
206 }
207
208 /** Implementation of {@link PVCoordinatesProvider} that throws an {@link OrekitException} exception.
209 *
210 */
211 public static class InvalidPVProvider implements PVCoordinatesProvider {
212
213 /** Empty constructor.
214 * <p>
215 * This constructor is not strictly necessary, but it prevents spurious
216 * javadoc warnings with JDK 18 and later.
217 * </p>
218 * @since 12.0
219 */
220 public InvalidPVProvider() {
221 // nothing to do
222 }
223
224 @Override
225 public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
226 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date);
227 }
228
229 }
230
231 }