1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.time.clocks;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.function.DoubleFunction;
25
26 import org.hipparchus.CalculusFieldElement;
27 import org.hipparchus.Field;
28 import org.hipparchus.analysis.differentiation.Gradient;
29 import org.hipparchus.analysis.differentiation.GradientField;
30 import org.hipparchus.analysis.polynomials.PolynomialFunction;
31 import org.hipparchus.util.FastMath;
32 import org.hipparchus.util.MathArrays;
33 import org.orekit.errors.OrekitException;
34 import org.orekit.errors.OrekitMessages;
35 import org.orekit.time.AbsoluteDate;
36 import org.orekit.time.FieldAbsoluteDate;
37 import org.orekit.time.TimeInterval;
38 import org.orekit.utils.drivers.ParameterDriver;
39
40
41
42
43
44
45
46 public class PolynomialClockModel implements ClockModel {
47
48
49
50
51
52
53
54
55 private static final double CLOCK_OFFSET_SCALE = FastMath.scalb(1.0, -10);
56
57
58 private final String prefix;
59
60
61 private final List<ParameterDriver> terms;
62
63
64
65
66 private final Map<Field<? extends CalculusFieldElement<?>>, PolynomialFieldClockModel<?>> fieldModels;
67
68
69
70
71
72 public PolynomialClockModel(final AbsoluteDate referenceDate,
73 final String prefix) {
74 this(referenceDate, prefix, 0.0);
75 }
76
77
78
79
80
81
82 public PolynomialClockModel(final AbsoluteDate referenceDate, final String prefix,
83 final double... terms) {
84
85 this.prefix = prefix;
86
87
88 final AbsoluteDate safeReferenceDate;
89 if (referenceDate == null) {
90 if (terms.length == 1) {
91
92
93 safeReferenceDate = AbsoluteDate.ARBITRARY_EPOCH;
94 } else {
95 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
96 prefix + getAcceptedTermSuffix(0));
97 }
98 } else {
99 safeReferenceDate = referenceDate;
100 }
101
102 final List<ParameterDriver> convertedTerms = new ArrayList<>();
103 for (int i = 0; i < terms.length; ++i) {
104 final String name = prefix + getAcceptedTermSuffix(i);
105 final ParameterDriver parameterTerm =
106 new ParameterDriver(name, 0.0, CLOCK_OFFSET_SCALE,
107 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
108 TimeInterval.UNLIMITED);
109 parameterTerm.setValue(terms[i]);
110 parameterTerm.setReferenceDate(safeReferenceDate);
111 convertedTerms.add(parameterTerm);
112 }
113 this.terms = convertedTerms;
114
115 this.fieldModels = new HashMap<>();
116
117 }
118
119
120
121
122
123
124
125
126
127
128 public PolynomialClockModel(final ParameterDriver... terms) {
129
130
131 if (terms.length == 0) {
132 prefix = "";
133 } else {
134 final String firstName = terms[0].getName();
135 final String firstSuffix = getAcceptedTermSuffix(0);
136 if (firstName.endsWith(firstSuffix)) {
137 prefix = firstName.substring(0, firstName.length() - firstSuffix.length());
138 } else {
139
140 prefix = firstName;
141 }
142 }
143
144 int idx = 0;
145 for (final ParameterDriver term : terms) {
146 final String acceptedName = prefix + getAcceptedTermSuffix(idx);
147 if (!term.getName().equals(acceptedName)) {
148 throw new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
149 term.getName(), acceptedName);
150 }
151 idx++;
152 }
153 this.terms = Arrays.asList(terms);
154
155 this.fieldModels = new HashMap<>();
156
157 }
158
159
160
161
162
163
164 public PolynomialClockModel(final List<ParameterDriver> terms) {
165 this(terms.toArray(new ParameterDriver[0]));
166 }
167
168
169
170
171
172
173 @Override
174 public AbsoluteDate getValidityStart() {
175 return terms.getFirst().getValidity().getStartDate();
176 }
177
178
179
180
181
182
183 @Override
184 public AbsoluteDate getValidityEnd() {
185 return terms.getFirst().getValidity().getEndDate();
186 }
187
188
189 @Override
190 public List<ParameterDriver> getParametersDrivers() {
191 return terms;
192 }
193
194
195
196
197
198
199
200
201 public void addParameterDriver(final int index, final ParameterDriver driver) {
202 final List<ParameterDriver> parameters = getParametersDrivers();
203 if (parameters.size() < index) {
204
205 addParameterDriver(index - 1, null);
206
207 addParameterDriver(index, driver);
208 } else if (parameters.size() == index) {
209 if (driver != null) {
210
211 parameters.add(driver);
212 } else {
213
214 final ParameterDriver empty = new ParameterDriver(prefix + getAcceptedTermSuffix(index),
215 0.0, CLOCK_OFFSET_SCALE,
216 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
217 TimeInterval.UNLIMITED);
218 empty.setReferenceDate(AbsoluteDate.ARBITRARY_EPOCH);
219 parameters.add(empty);
220 }
221 }
222 }
223
224
225 @Override
226 public ClockOffset getOffset(final AbsoluteDate date) {
227 final double[] result = new double[3];
228 if (terms.isEmpty()) {
229 return new ClockOffset(date, result);
230 }
231
232 final AbsoluteDate referenceDate = terms.getFirst().getReferenceDate();
233 final double dt;
234 if (referenceDate == null) {
235 if (terms.size() == 1) {
236
237 dt = 0;
238 } else {
239 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
240 terms.getFirst().getName());
241 }
242 } else {
243 dt = date.durationFrom(referenceDate);
244 }
245
246 final double[] convertedTerms = terms.
247 stream().
248 map(ParameterDriver::getValue).
249 mapToDouble(Double::doubleValue).
250 toArray();
251
252 PolynomialFunction function = new PolynomialFunction(convertedTerms);
253
254 for (int ii = 0; ii < 3; ii++) {
255 result[ii] = function.value(dt);
256 function = function.polynomialDerivative();
257 }
258 return new ClockOffset(date, result);
259 }
260
261
262 @Override
263 public <T extends CalculusFieldElement<T>> PolynomialFieldClockModel<T> toField(final DoubleFunction<T> converter) {
264
265 return (PolynomialFieldClockModel<T>) fieldModels.computeIfAbsent(converter.apply(0.0).getField(),
266 f -> buildFieldModel(converter));
267 }
268
269
270
271
272
273
274
275
276 private <T extends CalculusFieldElement<T>> PolynomialFieldClockModel<T> buildFieldModel(final DoubleFunction<T> converter) {
277
278 final Field<T> field = converter.apply(0.0).getField();
279
280
281 if (terms.isEmpty()) {
282 return new PolynomialFieldClockModel<>(FieldAbsoluteDate.getArbitraryEpoch(field), "");
283 }
284
285
286 if (terms.size() > 1 && terms.getFirst().getReferenceDate() == null) {
287 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
288 terms.getFirst().getName());
289 }
290 final FieldAbsoluteDate<T> fieldDate =
291 new FieldAbsoluteDate<>(field, terms.getFirst().getReferenceDate());
292
293
294 final T[] fieldTerms = MathArrays.buildArray(field, terms.size());
295 for (int i = 0; i < terms.size(); ++i) {
296 fieldTerms[i] = converter.apply(terms.get(i).getValue());
297 }
298
299 return new PolynomialFieldClockModel<>(fieldDate, prefix, fieldTerms);
300
301 }
302
303
304 @Override
305 public PolynomialFieldClockModel<Gradient> toGradient(final int freeParameters, final Map<String, Integer> indices) {
306
307 final GradientField field = GradientField.getField(freeParameters);
308
309
310 if (terms.isEmpty()) {
311 return new PolynomialFieldClockModel<>(FieldAbsoluteDate.getArbitraryEpoch(field), "");
312 }
313
314
315 if (terms.size() > 1 && terms.getFirst().getReferenceDate() == null) {
316 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
317 terms.getFirst().getName());
318 }
319 final FieldAbsoluteDate<Gradient> fieldDate =
320 new FieldAbsoluteDate<>(field, terms.getFirst().getReferenceDate());
321
322
323 final Gradient[] fieldTerms = new Gradient[terms.size()];
324 for (int i = 0; i < terms.size(); ++i) {
325 fieldTerms[i] = terms.get(i).getValue(freeParameters, indices);
326 }
327
328 return new PolynomialFieldClockModel<>(fieldDate, prefix, fieldTerms);
329
330 }
331
332 }