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.propagation.semianalytical.dsst;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26
27 import org.hipparchus.linear.RealMatrix;
28 import org.hipparchus.ode.ODEIntegrator;
29 import org.hipparchus.ode.ODEStateAndDerivative;
30 import org.hipparchus.ode.sampling.ODEStateInterpolator;
31 import org.hipparchus.ode.sampling.ODEStepHandler;
32 import org.orekit.annotation.DefaultDataContext;
33 import org.orekit.attitudes.Attitude;
34 import org.orekit.attitudes.AttitudeProvider;
35 import org.orekit.data.DataContext;
36 import org.orekit.errors.OrekitException;
37 import org.orekit.errors.OrekitMessages;
38 import org.orekit.frames.Frame;
39 import org.orekit.orbits.EquinoctialOrbit;
40 import org.orekit.orbits.Orbit;
41 import org.orekit.orbits.OrbitType;
42 import org.orekit.orbits.PositionAngleType;
43 import org.orekit.propagation.CartesianToleranceProvider;
44 import org.orekit.propagation.MatricesHarvester;
45 import org.orekit.propagation.PropagationType;
46 import org.orekit.propagation.Propagator;
47 import org.orekit.propagation.SpacecraftState;
48 import org.orekit.propagation.ToleranceProvider;
49 import org.orekit.propagation.conversion.osc2mean.DSSTTheory;
50 import org.orekit.propagation.conversion.osc2mean.FixedPointConverter;
51 import org.orekit.propagation.conversion.osc2mean.MeanTheory;
52 import org.orekit.propagation.conversion.osc2mean.OsculatingToMeanConverter;
53 import org.orekit.propagation.integration.AbstractIntegratedPropagator;
54 import org.orekit.propagation.integration.AdditionalDerivativesProvider;
55 import org.orekit.propagation.integration.StateMapper;
56 import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
57 import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction;
58 import org.orekit.propagation.semianalytical.dsst.forces.ShortPeriodTerms;
59 import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
60 import org.orekit.propagation.semianalytical.dsst.utilities.FixedNumberInterpolationGrid;
61 import org.orekit.propagation.semianalytical.dsst.utilities.InterpolationGrid;
62 import org.orekit.propagation.semianalytical.dsst.utilities.MaxGapInterpolationGrid;
63 import org.orekit.time.AbsoluteDate;
64 import org.orekit.utils.DataDictionary;
65 import org.orekit.utils.DoubleArrayDictionary;
66 import org.orekit.utils.PVCoordinates;
67 import org.orekit.utils.ParameterDriver;
68 import org.orekit.utils.ParameterDriversList;
69 import org.orekit.utils.ParameterDriversList.DelegatingDriver;
70 import org.orekit.utils.ParameterObserver;
71 import org.orekit.utils.TimeSpanMap;
72 import org.orekit.utils.TimeSpanMap.Span;
73
74 /**
75 * This class propagates {@link org.orekit.orbits.Orbit orbits} using the DSST theory.
76 * <p>
77 * Whereas analytical propagators are configured only thanks to their various
78 * constructors and can be used immediately after construction, such a semianalytical
79 * propagator configuration involves setting several parameters between construction
80 * time and propagation time, just as numerical propagators.
81 * </p>
82 * <p>
83 * The configuration parameters that can be set are:
84 * </p>
85 * <ul>
86 * <li>the initial spacecraft state ({@link #setInitialState(SpacecraftState)})</li>
87 * <li>the various force models ({@link #addForceModel(DSSTForceModel)},
88 * {@link #removeForceModels()})</li>
89 * <li>the discrete events that should be triggered during propagation (
90 * {@link #addEventDetector(org.orekit.propagation.events.EventDetector)},
91 * {@link #clearEventsDetectors()})</li>
92 * <li>the binding logic with the rest of the application ({@link #getMultiplexer()})</li>
93 * </ul>
94 * <p>
95 * From these configuration parameters, only the initial state is mandatory.
96 * The default propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial}
97 * parameters with {@link PositionAngleType#TRUE true} longitude argument.
98 * The central attraction coefficient used to define the initial orbit will be used.
99 * However, specifying only the initial state would mean the propagator would use
100 * only Keplerian forces. In this case, the simpler
101 * {@link org.orekit.propagation.analytical.KeplerianPropagator KeplerianPropagator}
102 * class would be more effective.
103 * </p>
104 * <p>
105 * The underlying numerical integrator set up in the constructor may also have
106 * its own configuration parameters. Typical configuration parameters for adaptive
107 * stepsize integrators are the min, max and perhaps start step size as well as
108 * the absolute and/or relative errors thresholds.
109 * </p>
110 * <p>
111 * The state that is seen by the integrator is a simple six elements double array.
112 * These six elements are:
113 * <ul>
114 * <li>the {@link org.orekit.orbits.EquinoctialOrbit equinoctial orbit parameters}
115 * (a, e<sub>x</sub>, e<sub>y</sub>, h<sub>x</sub>, h<sub>y</sub>, λ<sub>m</sub>)
116 * in meters and radians,</li>
117 * </ul>
118 *
119 * <p>By default, at the end of the propagation, the propagator resets the initial state to the final state,
120 * thus allowing a new propagation to be started from there without recomputing the part already performed.
121 * This behaviour can be chenged by calling {@link #setResetAtEnd(boolean)}.
122 * </p>
123 * <p>Beware the same instance cannot be used simultaneously by different threads, the class is <em>not</em>
124 * thread-safe.</p>
125 *
126 * @see SpacecraftState
127 * @see DSSTForceModel
128 * @author Romain Di Costanzo
129 * @author Pascal Parraud
130 */
131 public class DSSTPropagator extends AbstractIntegratedPropagator {
132
133 /** Retrograde factor I.
134 * <p>
135 * DSST model needs equinoctial orbit as internal representation.
136 * Classical equinoctial elements have discontinuities when inclination
137 * is close to zero. In this representation, I = +1. <br>
138 * To avoid this discontinuity, another representation exists and equinoctial
139 * elements can be expressed in a different way, called "retrograde" orbit.
140 * This implies I = -1. <br>
141 * As Orekit doesn't implement the retrograde orbit, I is always set to +1.
142 * But for the sake of consistency with the theory, the retrograde factor
143 * has been kept in the formulas.
144 * </p>
145 */
146 private static final int I = 1;
147
148 /** Default value for epsilon. */
149 private static final double EPSILON_DEFAULT = 1.0e-13;
150
151 /** Default value for maxIterations. */
152 private static final int MAX_ITERATIONS_DEFAULT = 200;
153
154 /** Number of grid points per integration step to be used in interpolation of short periodics coefficients.*/
155 private static final int INTERPOLATION_POINTS_PER_STEP = 3;
156
157 /** Flag specifying whether the initial orbital state is given with osculating elements. */
158 private boolean initialIsOsculating;
159
160 /** Force models used to compute short periodic terms. */
161 private final List<DSSTForceModel> forceModels;
162
163 /** State mapper holding the force models. */
164 private MeanPlusShortPeriodicMapper mapper;
165
166 /** Generator for the interpolation grid. */
167 private InterpolationGrid interpolationgrid;
168
169 /**
170 * Same as {@link org.orekit.propagation.AbstractPropagator#harvester} but with the
171 * more specific type. Saved to avoid a cast.
172 */
173 private DSSTHarvester harvester;
174
175 /** Create a new instance of DSSTPropagator.
176 * <p>
177 * After creation, there are no perturbing forces at all.
178 * This means that if {@link #addForceModel addForceModel}
179 * is not called after creation, the integrated orbit will
180 * follow a Keplerian evolution only.
181 * </p>
182 *
183 * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
184 *
185 * @param integrator numerical integrator to use for propagation.
186 * @param propagationType type of orbit to output (mean or osculating).
187 * @see #DSSTPropagator(ODEIntegrator, PropagationType, AttitudeProvider)
188 */
189 @DefaultDataContext
190 public DSSTPropagator(final ODEIntegrator integrator, final PropagationType propagationType) {
191 this(integrator, propagationType,
192 Propagator.getDefaultLaw(DataContext.getDefault().getFrames()));
193 }
194
195 /** Create a new instance of DSSTPropagator.
196 * <p>
197 * After creation, there are no perturbing forces at all.
198 * This means that if {@link #addForceModel addForceModel}
199 * is not called after creation, the integrated orbit will
200 * follow a Keplerian evolution only.
201 * </p>
202 * @param integrator numerical integrator to use for propagation.
203 * @param propagationType type of orbit to output (mean or osculating).
204 * @param attitudeProvider the attitude law.
205 * @since 10.1
206 */
207 public DSSTPropagator(final ODEIntegrator integrator,
208 final PropagationType propagationType,
209 final AttitudeProvider attitudeProvider) {
210 super(integrator, propagationType);
211 forceModels = new ArrayList<>();
212 initMapper();
213 // DSST uses only equinoctial orbits and mean longitude argument
214 setOrbitType(OrbitType.EQUINOCTIAL);
215 setPositionAngleType(PositionAngleType.MEAN);
216 setAttitudeProvider(attitudeProvider);
217 setInterpolationGridToFixedNumberOfPoints(INTERPOLATION_POINTS_PER_STEP);
218 }
219
220
221 /** Create a new instance of DSSTPropagator.
222 * <p>
223 * After creation, there are no perturbing forces at all.
224 * This means that if {@link #addForceModel addForceModel}
225 * is not called after creation, the integrated orbit will
226 * follow a Keplerian evolution only. Only the mean orbits
227 * will be generated.
228 * </p>
229 *
230 * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
231 *
232 * @param integrator numerical integrator to use for propagation.
233 * @see #DSSTPropagator(ODEIntegrator, PropagationType, AttitudeProvider)
234 */
235 @DefaultDataContext
236 public DSSTPropagator(final ODEIntegrator integrator) {
237 this(integrator, PropagationType.MEAN);
238 }
239
240 /** Set the central attraction coefficient μ.
241 * <p>
242 * Setting the central attraction coefficient is
243 * equivalent to {@link #addForceModel(DSSTForceModel) add}
244 * a {@link DSSTNewtonianAttraction} force model.
245 * </p>
246 * @param mu central attraction coefficient (m³/s²)
247 * @see #addForceModel(DSSTForceModel)
248 * @see #getAllForceModels()
249 */
250 @Override
251 public void setMu(final double mu) {
252 addForceModel(new DSSTNewtonianAttraction(mu));
253 }
254
255 /** Set the central attraction coefficient μ only in upper class.
256 * @param mu central attraction coefficient (m³/s²)
257 */
258 private void superSetMu(final double mu) {
259 super.setMu(mu);
260 }
261
262 /** Check if Newtonian attraction force model is available.
263 * <p>
264 * Newtonian attraction is always the last force model in the list.
265 * </p>
266 * @return true if Newtonian attraction force model is available
267 */
268 private boolean hasNewtonianAttraction() {
269 final int last = forceModels.size() - 1;
270 return last >= 0 && forceModels.get(last) instanceof DSSTNewtonianAttraction;
271 }
272
273 /** Set the initial state with osculating orbital elements.
274 * @param initialState initial state (defined with osculating elements)
275 */
276 public void setInitialState(final SpacecraftState initialState) {
277 setInitialState(initialState, PropagationType.OSCULATING);
278 }
279
280 /** Set the initial state.
281 * @param initialState initial state
282 * @param stateType defined if the orbital state is defined with osculating or mean elements
283 */
284 public void setInitialState(final SpacecraftState initialState,
285 final PropagationType stateType) {
286 resetInitialState(initialState, stateType);
287 }
288
289 /** Reset the initial state.
290 *
291 * @param state new initial state
292 */
293 @Override
294 public void resetInitialState(final SpacecraftState state) {
295 super.resetInitialState(state);
296 if (!hasNewtonianAttraction()) {
297 // use the state to define central attraction
298 setMu(state.getOrbit().getMu());
299 }
300 super.setStartDate(state.getDate());
301 }
302
303 /** {@inheritDoc}.
304 *
305 * <p>Change parameter {@link #initialIsOsculating()} accordingly
306 * @since 12.1.3
307 */
308 @Override
309 public void resetInitialState(final SpacecraftState state, final PropagationType stateType) {
310 // Reset initial state
311 resetInitialState(state);
312
313 // Change state of initial osculating, if needed
314 initialIsOsculating = stateType == PropagationType.OSCULATING;
315 }
316
317 /** Set the selected short periodic coefficients that must be stored as additional states.
318 * @param selectedCoefficients short periodic coefficients that must be stored as additional states
319 * (null means no coefficients are selected, empty set means all coefficients are selected)
320 */
321 public void setSelectedCoefficients(final Set<String> selectedCoefficients) {
322 mapper.setSelectedCoefficients(selectedCoefficients == null ? null : new HashSet<>(selectedCoefficients));
323 }
324
325 /** Get the selected short periodic coefficients that must be stored as additional states.
326 * @return short periodic coefficients that must be stored as additional states
327 * (null means no coefficients are selected, empty set means all coefficients are selected)
328 */
329 public Set<String> getSelectedCoefficients() {
330 final Set<String> set = mapper.getSelectedCoefficients();
331 return set == null ? null : Collections.unmodifiableSet(set);
332 }
333
334 /** Get the names of the parameters in the matrix returned by {@link MatricesHarvester#getParametersJacobian}.
335 * @return names of the parameters (i.e. columns) of the Jacobian matrix
336 */
337 protected List<String> getJacobiansColumnsNames() {
338 final List<String> columnsNames = new ArrayList<>();
339 for (final DSSTForceModel forceModel : getAllForceModels()) {
340 for (final ParameterDriver driver : forceModel.getParametersDrivers()) {
341 if (driver.isSelected() && !columnsNames.contains(driver.getNamesSpanMap().getFirstSpan().getData())) {
342 // As driver with same name should have same NamesSpanMap we only check if the first span is present,
343 // if not we add all span names to columnsNames
344 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
345 columnsNames.add(span.getData());
346 }
347 }
348 }
349 }
350 Collections.sort(columnsNames);
351 return columnsNames;
352 }
353
354 /** {@inheritDoc} */
355 @Override
356 public DSSTHarvester setupMatricesComputation(
357 final String stmName,
358 final RealMatrix initialStm,
359 final DoubleArrayDictionary initialJacobianColumns) {
360
361 if (stmName == null) {
362 throw new OrekitException(OrekitMessages.NULL_ARGUMENT, "stmName");
363 }
364 final DSSTHarvester dsstHarvester =
365 createHarvester(stmName, initialStm, initialJacobianColumns);
366 return this.harvester = dsstHarvester;
367 }
368
369 /** {@inheritDoc} */
370 @Override
371 protected DSSTHarvester createHarvester(final String stmName, final RealMatrix initialStm,
372 final DoubleArrayDictionary initialJacobianColumns) {
373 final DSSTHarvester dsstHarvester =
374 new DSSTHarvester(this, stmName, initialStm, initialJacobianColumns);
375 this.harvester = dsstHarvester;
376 return dsstHarvester;
377 }
378
379 /** {@inheritDoc} */
380 @Override
381 protected DSSTHarvester getHarvester() {
382 return harvester;
383 }
384
385 /** {@inheritDoc} */
386 @Override
387 protected void setUpStmAndJacobianGenerators() {
388
389 final DSSTHarvester dsstHarvester = getHarvester();
390 if (dsstHarvester != null) {
391
392 // set up the additional equations and additional state providers
393 final DSSTStateTransitionMatrixGenerator stmGenerator = setUpStmGenerator();
394 setUpRegularParametersJacobiansColumns(stmGenerator);
395
396 // as we are now starting the propagation, everything is configured
397 // we can freeze the names in the harvester
398 dsstHarvester.freezeColumnsNames();
399
400 }
401
402 }
403
404 /** Set up the State Transition Matrix Generator.
405 * @return State Transition Matrix Generator
406 * @since 11.1
407 */
408 private DSSTStateTransitionMatrixGenerator setUpStmGenerator() {
409
410 final DSSTHarvester dsstHarvester = getHarvester();
411
412 // add the STM generator corresponding to the current settings, and setup state accordingly
413 DSSTStateTransitionMatrixGenerator stmGenerator = null;
414 for (final AdditionalDerivativesProvider equations : getAdditionalDerivativesProviders()) {
415 if (equations instanceof DSSTStateTransitionMatrixGenerator &&
416 equations.getName().equals(dsstHarvester.getStmName())) {
417 // the STM generator has already been set up in a previous propagation
418 stmGenerator = (DSSTStateTransitionMatrixGenerator) equations;
419 break;
420 }
421 }
422 if (stmGenerator == null) {
423 // this is the first time we need the STM generate, create it
424 stmGenerator = new DSSTStateTransitionMatrixGenerator(dsstHarvester.getStmName(),
425 getAllForceModels(),
426 getAttitudeProvider(),
427 getPropagationType());
428 addAdditionalDerivativesProvider(stmGenerator);
429 }
430
431 if (!getInitialIntegrationState().hasAdditionalData(dsstHarvester.getStmName())) {
432 // add the initial State Transition Matrix if it is not already there
433 // (perhaps due to a previous propagation)
434 setInitialState(stmGenerator.setInitialStateTransitionMatrix(getInitialState(),
435 dsstHarvester.getInitialStateTransitionMatrix()),
436 initialIsOsculating() ? PropagationType.OSCULATING : PropagationType.MEAN);
437 }
438
439 return stmGenerator;
440
441 }
442
443 /** Set up the Jacobians columns generator for regular parameters.
444 * @param stmGenerator generator for the State Transition Matrix
445 * @since 11.1
446 */
447 private void setUpRegularParametersJacobiansColumns(final DSSTStateTransitionMatrixGenerator stmGenerator) {
448
449 // first pass: gather all parameters (excluding trigger dates), binding similar names together
450 final ParameterDriversList selected = new ParameterDriversList();
451 for (final DSSTForceModel forceModel : getAllForceModels()) {
452 for (final ParameterDriver driver : forceModel.getParametersDrivers()) {
453 selected.add(driver);
454 }
455 }
456
457 // second pass: now that shared parameter names are bound together,
458 // their selections status have been synchronized, we can filter them
459 selected.filter(true);
460
461 // third pass: sort parameters lexicographically
462 selected.sort();
463
464 // add the Jacobians column generators corresponding to parameters, and setup state accordingly
465 for (final DelegatingDriver driver : selected.getDrivers()) {
466
467 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
468 DSSTIntegrableJacobianColumnGenerator generator = null;
469
470 // check if we already have set up the providers
471 for (final AdditionalDerivativesProvider provider : getAdditionalDerivativesProviders()) {
472 if (provider instanceof DSSTIntegrableJacobianColumnGenerator &&
473 provider.getName().equals(span.getData())) {
474 // the Jacobian column generator has already been set up in a previous propagation
475 generator = (DSSTIntegrableJacobianColumnGenerator) provider;
476 break;
477 }
478 }
479
480 if (generator == null) {
481 // this is the first time we need the Jacobian column generator, create it
482 generator = new DSSTIntegrableJacobianColumnGenerator(stmGenerator, span.getData());
483 addAdditionalDerivativesProvider(generator);
484 }
485
486 if (!getInitialIntegrationState().hasAdditionalData(span.getData())) {
487 // add the initial Jacobian column if it is not already there
488 // (perhaps due to a previous propagation)
489 setInitialState(getInitialState().addAdditionalData(span.getData(),
490 getHarvester().getInitialJacobianColumn(span.getData())),
491 initialIsOsculating() ? PropagationType.OSCULATING : PropagationType.MEAN);
492 }
493 }
494
495 }
496
497 }
498
499 /** Check if the initial state is provided in osculating elements.
500 * @return true if initial state is provided in osculating elements
501 */
502 public boolean initialIsOsculating() {
503 return initialIsOsculating;
504 }
505
506 /** Set the interpolation grid generator.
507 * <p>
508 * The generator will create an interpolation grid with a fixed
509 * number of points for each mean element integration step.
510 * </p>
511 * <p>
512 * If neither {@link #setInterpolationGridToFixedNumberOfPoints(int)}
513 * nor {@link #setInterpolationGridToMaxTimeGap(double)} has been called,
514 * by default the propagator is set as to 3 interpolations points per step.
515 * </p>
516 * @param interpolationPoints number of interpolation points at
517 * each integration step
518 * @see #setInterpolationGridToMaxTimeGap(double)
519 * @since 7.1
520 */
521 public void setInterpolationGridToFixedNumberOfPoints(final int interpolationPoints) {
522 interpolationgrid = new FixedNumberInterpolationGrid(interpolationPoints);
523 }
524
525 /** Set the interpolation grid generator.
526 * <p>
527 * The generator will create an interpolation grid with a maximum
528 * time gap between interpolation points.
529 * </p>
530 * <p>
531 * If neither {@link #setInterpolationGridToFixedNumberOfPoints(int)}
532 * nor {@link #setInterpolationGridToMaxTimeGap(double)} has been called,
533 * by default the propagator is set as to 3 interpolations points per step.
534 * </p>
535 * @param maxGap maximum time gap between interpolation points (seconds)
536 * @see #setInterpolationGridToFixedNumberOfPoints(int)
537 * @since 7.1
538 */
539 public void setInterpolationGridToMaxTimeGap(final double maxGap) {
540 interpolationgrid = new MaxGapInterpolationGrid(maxGap);
541 }
542
543 /** Add a force model to the global perturbation model.
544 * <p>
545 * If this method is not called at all,
546 * the integrated orbit will follow a Keplerian evolution only.
547 * </p>
548 * @param force perturbing {@link DSSTForceModel force} to add
549 * @see #removeForceModels()
550 * @see #setMu(double)
551 */
552 public void addForceModel(final DSSTForceModel force) {
553
554 if (force instanceof DSSTNewtonianAttraction) {
555 // we want to add the central attraction force model
556
557 // ensure we are notified of any mu change
558 force.getParametersDrivers().get(0).addObserver(new ParameterObserver() {
559 /** {@inheritDoc} */
560 @Override
561 public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
562 // mu PDriver should have only 1 span
563 superSetMu(driver.getValue());
564 }
565 /** {@inheritDoc} */
566 @Override
567 public void valueSpanMapChanged(final TimeSpanMap<Double> previousValue, final ParameterDriver driver) {
568 // mu PDriver should have only 1 span
569 superSetMu(driver.getValue());
570 }
571 });
572
573 if (hasNewtonianAttraction()) {
574 // there is already a central attraction model, replace it
575 forceModels.set(forceModels.size() - 1, force);
576 } else {
577 // there are no central attraction model yet, add it at the end of the list
578 forceModels.add(force);
579 }
580 } else {
581 // we want to add a perturbing force model
582 if (hasNewtonianAttraction()) {
583 // insert the new force model before Newtonian attraction,
584 // which should always be the last one in the list
585 forceModels.add(forceModels.size() - 1, force);
586 } else {
587 // we only have perturbing force models up to now, just append at the end of the list
588 forceModels.add(force);
589 }
590 }
591
592 force.registerAttitudeProvider(getAttitudeProvider());
593
594 }
595
596 /** Remove all perturbing force models from the global perturbation model
597 * (except central attraction).
598 * <p>
599 * Once all perturbing forces have been removed (and as long as no new force model is added),
600 * the integrated orbit will follow a Keplerian evolution only.
601 * </p>
602 * @see #addForceModel(DSSTForceModel)
603 */
604 public void removeForceModels() {
605 final int last = forceModels.size() - 1;
606 if (hasNewtonianAttraction()) {
607 // preserve the Newtonian attraction model at the end
608 final DSSTForceModel newton = forceModels.get(last);
609 forceModels.clear();
610 forceModels.add(newton);
611 } else {
612 forceModels.clear();
613 }
614 }
615
616 /** Get all the force models, perturbing forces and Newtonian attraction included.
617 * @return list of perturbing force models, with Newtonian attraction being the
618 * last one
619 * @see #addForceModel(DSSTForceModel)
620 * @see #setMu(double)
621 */
622 public List<DSSTForceModel> getAllForceModels() {
623 return Collections.unmodifiableList(forceModels);
624 }
625
626 /** Get propagation parameter type.
627 * @return orbit type used for propagation
628 */
629 @Override
630 public OrbitType getOrbitType() {
631 return super.getOrbitType();
632 }
633
634 /** Get propagation parameter type.
635 * @return angle type to use for propagation
636 */
637 @Override
638 public PositionAngleType getPositionAngleType() {
639 return super.getPositionAngleType();
640 }
641
642 /** Conversion from mean to osculating orbit.
643 * <p>
644 * Compute osculating state <b>in a DSST sense</b>, corresponding to the
645 * mean SpacecraftState in input, and according to the Force models taken
646 * into account.
647 * </p><p>
648 * Since the osculating state is obtained by adding short-periodic variation
649 * of each force model, the resulting output will depend on the
650 * force models parameterized in input.
651 * </p>
652 * @param mean Mean state to convert
653 * @param forces Forces to take into account
654 * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
655 * like atmospheric drag, radiation pressure or specific user-defined models)
656 * @return osculating state in a DSST sense
657 */
658 public static SpacecraftState computeOsculatingState(final SpacecraftState mean,
659 final AttitudeProvider attitudeProvider,
660 final Collection<DSSTForceModel> forces) {
661
662 //Create the auxiliary object
663 final AuxiliaryElements aux = new AuxiliaryElements(mean.getOrbit(), I);
664
665 // Set the force models
666 final List<ShortPeriodTerms> shortPeriodTerms = new ArrayList<>();
667 for (final DSSTForceModel force : forces) {
668 force.registerAttitudeProvider(attitudeProvider);
669 shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(mean.getDate())));
670 force.updateShortPeriodTerms(force.getParametersAllValues(), mean);
671 }
672
673 final EquinoctialOrbit osculatingOrbit = computeOsculatingOrbit(mean, shortPeriodTerms);
674
675 return new SpacecraftState(osculatingOrbit, mean.getAttitude(), mean.getMass(),
676 mean.getAdditionalDataValues(), mean.getAdditionalStatesDerivatives());
677
678 }
679
680 /** Conversion from osculating to mean orbit.
681 * <p>
682 * Compute mean state <b>in a DSST sense</b>, corresponding to the
683 * osculating SpacecraftState in input, and according to the Force models
684 * taken into account.
685 * </p><p>
686 * Since the osculating state is obtained with the computation of
687 * short-periodic variation of each force model, the resulting output will
688 * depend on the force models parameterized in input.
689 * </p><p>
690 * The computation is done through a fixed-point iteration process.
691 * </p>
692 * @param osculating Osculating state to convert
693 * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
694 * like atmospheric drag, radiation pressure or specific user-defined models)
695 * @param forceModels Forces to take into account
696 * @return mean state in a DSST sense
697 */
698 public static SpacecraftState computeMeanState(final SpacecraftState osculating,
699 final AttitudeProvider attitudeProvider,
700 final Collection<DSSTForceModel> forceModels) {
701 return computeMeanState(osculating, attitudeProvider, forceModels, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
702 }
703
704 /** Conversion from osculating to mean orbit.
705 * <p>
706 * Compute mean state <b>in a DSST sense</b>, corresponding to the
707 * osculating SpacecraftState in input, and according to the Force models
708 * taken into account.
709 * </p><p>
710 * Since the osculating state is obtained with the computation of
711 * short-periodic variation of each force model, the resulting output will
712 * depend on the force models parameterized in input.
713 * </p><p>
714 * The computation is done through a fixed-point iteration process.
715 * </p>
716 * @param osculating Osculating state to convert
717 * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
718 * like atmospheric drag, radiation pressure or specific user-defined models)
719 * @param forceModels Forces to take into account
720 * @param epsilon convergence threshold for mean parameters conversion
721 * @param maxIterations maximum iterations for mean parameters conversion
722 * @return mean state in a DSST sense
723 * @since 10.1
724 */
725 public static SpacecraftState computeMeanState(final SpacecraftState osculating,
726 final AttitudeProvider attitudeProvider,
727 final Collection<DSSTForceModel> forceModels,
728 final double epsilon,
729 final int maxIterations) {
730 final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
731 FixedPointConverter.DEFAULT_DAMPING);
732 return computeMeanState(osculating, attitudeProvider, forceModels, converter);
733 }
734
735 /** Conversion from osculating to mean orbit.
736 * <p>
737 * Compute mean state <b>in a DSST sense</b>, corresponding to the
738 * osculating SpacecraftState in input, and according to the Force models
739 * taken into account.
740 * </p><p>
741 * Since the osculating state is obtained with the computation of
742 * short-periodic variation of each force model, the resulting output will
743 * depend on the force models parameterized in input.
744 * </p><p>
745 * The computation is done using the given osculating to mean orbit converter.
746 * </p>
747 * @param osculating osculating state to convert
748 * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
749 * like atmospheric drag, radiation pressure or specific user-defined models)
750 * @param forceModels forces to take into account
751 * @param converter osculating to mean orbit converter
752 * @return mean state in a DSST sense
753 * @since 13.0
754 */
755 public static SpacecraftState computeMeanState(final SpacecraftState osculating,
756 final AttitudeProvider attitudeProvider,
757 final Collection<DSSTForceModel> forceModels,
758 final OsculatingToMeanConverter converter) {
759
760 final MeanTheory theory = new DSSTTheory(forceModels, attitudeProvider, osculating.getMass());
761 converter.setMeanTheory(theory);
762 final Orbit meanOrbit = converter.convertToMean(osculating.getOrbit());
763 return new SpacecraftState(meanOrbit, osculating.getAttitude(), osculating.getMass(),
764 osculating.getAdditionalDataValues(), osculating.getAdditionalStatesDerivatives());
765 }
766
767 /** Override the default value of the parameter.
768 * <p>
769 * By default, if the initial orbit is defined as osculating,
770 * it will be averaged over 2 satellite revolutions.
771 * This can be changed by using this method.
772 * </p>
773 * @param satelliteRevolution number of satellite revolutions to use for converting osculating to mean
774 * elements
775 */
776 public void setSatelliteRevolution(final int satelliteRevolution) {
777 mapper.setSatelliteRevolution(satelliteRevolution);
778 }
779
780 /** Get the number of satellite revolutions to use for converting osculating to mean elements.
781 * @return number of satellite revolutions to use for converting osculating to mean elements
782 */
783 public int getSatelliteRevolution() {
784 return mapper.getSatelliteRevolution();
785 }
786
787 /** Override the default value short periodic terms.
788 * <p>
789 * By default, short periodic terms are initialized before
790 * the numerical integration of the mean orbital elements.
791 * </p>
792 * @param shortPeriodTerms short periodic terms
793 */
794 public void setShortPeriodTerms(final List<ShortPeriodTerms> shortPeriodTerms) {
795 mapper.setShortPeriodTerms(shortPeriodTerms);
796 }
797
798 /** Get the short periodic terms.
799 * @return the short periodic terms
800 */
801 public List<ShortPeriodTerms> getShortPeriodTerms() {
802 return mapper.getShortPeriodTerms();
803 }
804
805 /** {@inheritDoc} */
806 @Override
807 public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
808 super.setAttitudeProvider(attitudeProvider);
809
810 //Register the attitude provider for each force model
811 for (final DSSTForceModel force : forceModels) {
812 force.registerAttitudeProvider(attitudeProvider);
813 }
814 }
815
816 /** Method called just before integration.
817 * <p>
818 * The default implementation does nothing, it may be specialized in subclasses.
819 * </p>
820 * @param initialState initial state
821 * @param tEnd target date at which state should be propagated
822 */
823 @Override
824 protected void beforeIntegration(final SpacecraftState initialState,
825 final AbsoluteDate tEnd) {
826 // If this method is updated also update DSSTStateTransitionMatrixGenerator.init(...)
827
828 // check if only mean elements must be used
829 final PropagationType type = getPropagationType();
830
831 // compute common auxiliary elements
832 final AuxiliaryElements aux = new AuxiliaryElements(initialState.getOrbit(), I);
833
834 // initialize all perturbing forces
835 final List<ShortPeriodTerms> shortPeriodTerms = new ArrayList<>();
836 for (final DSSTForceModel force : forceModels) {
837 shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(initialState.getDate())));
838 }
839 mapper.setShortPeriodTerms(shortPeriodTerms);
840
841 // if required, insert the special short periodics step handler
842 if (type == PropagationType.OSCULATING) {
843 final ShortPeriodicsHandler spHandler = new ShortPeriodicsHandler(forceModels);
844 // Compute short periodic coefficients for this point
845 for (DSSTForceModel forceModel : forceModels) {
846 forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(), initialState);
847 }
848 final Collection<ODEStepHandler> stepHandlers = new ArrayList<>();
849 stepHandlers.add(spHandler);
850 final ODEIntegrator integrator = getIntegrator();
851 final Collection<ODEStepHandler> existing = integrator.getStepHandlers();
852 stepHandlers.addAll(existing);
853
854 integrator.clearStepHandlers();
855
856 // add back the existing handlers after the short periodics one
857 for (final ODEStepHandler sp : stepHandlers) {
858 integrator.addStepHandler(sp);
859 }
860 }
861 }
862
863 /** {@inheritDoc} */
864 @Override
865 protected void afterIntegration() {
866 // remove the special short periodics step handler if added before
867 if (getPropagationType() == PropagationType.OSCULATING) {
868 final List<ODEStepHandler> preserved = new ArrayList<>();
869 final ODEIntegrator integrator = getIntegrator();
870 for (final ODEStepHandler sp : integrator.getStepHandlers()) {
871 if (!(sp instanceof ShortPeriodicsHandler)) {
872 preserved.add(sp);
873 }
874 }
875
876 // clear the list
877 integrator.clearStepHandlers();
878
879 // add back the step handlers that were important for the user
880 for (final ODEStepHandler sp : preserved) {
881 integrator.addStepHandler(sp);
882 }
883 }
884 }
885
886 /** Compute osculating state from mean state.
887 * <p>
888 * Compute and add the short periodic variation to the mean {@link SpacecraftState}.
889 * </p>
890 * @param meanState initial mean state
891 * @param shortPeriodTerms short period terms
892 * @return osculating state
893 */
894 private static EquinoctialOrbit computeOsculatingOrbit(final SpacecraftState meanState,
895 final List<ShortPeriodTerms> shortPeriodTerms) {
896
897 final double[] mean = new double[6];
898 final double[] meanDot = new double[6];
899 OrbitType.EQUINOCTIAL.mapOrbitToArray(meanState.getOrbit(), PositionAngleType.MEAN, mean, meanDot);
900 final double[] y = mean.clone();
901 for (final ShortPeriodTerms spt : shortPeriodTerms) {
902 final double[] shortPeriodic = spt.value(meanState.getOrbit());
903 for (int i = 0; i < shortPeriodic.length; i++) {
904 y[i] += shortPeriodic[i];
905 }
906 }
907 return (EquinoctialOrbit) OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, meanDot,
908 PositionAngleType.MEAN, meanState.getDate(),
909 meanState.getOrbit().getMu(), meanState.getFrame());
910 }
911
912 /** {@inheritDoc} */
913 @Override
914 protected SpacecraftState getInitialIntegrationState() {
915 if (initialIsOsculating) {
916 // the initial state is an osculating state,
917 // it must be converted to mean state
918 return computeMeanState(getInitialState(), getAttitudeProvider(), forceModels);
919 } else {
920 // the initial state is already a mean state
921 return getInitialState();
922 }
923 }
924
925 /** {@inheritDoc}
926 * <p>
927 * Note that for DSST, orbit type is hardcoded to {@link OrbitType#EQUINOCTIAL}
928 * and position angle type is hardcoded to {@link PositionAngleType#MEAN}, so
929 * the corresponding parameters are ignored.
930 * </p>
931 */
932 @Override
933 protected StateMapper createMapper(final AbsoluteDate referenceDate, final double mu,
934 final OrbitType ignoredOrbitType, final PositionAngleType ignoredPositionAngleType,
935 final AttitudeProvider attitudeProvider, final Frame frame) {
936
937 // create a mapper with the common settings provided as arguments
938 final MeanPlusShortPeriodicMapper newMapper =
939 new MeanPlusShortPeriodicMapper(referenceDate, mu, attitudeProvider, frame);
940
941 // copy the specific settings from the existing mapper
942 if (mapper != null) {
943 newMapper.setSatelliteRevolution(mapper.getSatelliteRevolution());
944 newMapper.setSelectedCoefficients(mapper.getSelectedCoefficients());
945 newMapper.setShortPeriodTerms(mapper.getShortPeriodTerms());
946 }
947
948 mapper = newMapper;
949 return mapper;
950
951 }
952
953
954 /** Get the short period terms value.
955 * @param meanState the mean state
956 * @return shortPeriodTerms short period terms
957 * @since 7.1
958 */
959 public double[] getShortPeriodTermsValue(final SpacecraftState meanState) {
960 final double[] sptValue = new double[6];
961
962 for (ShortPeriodTerms spt : mapper.getShortPeriodTerms()) {
963 final double[] shortPeriodic = spt.value(meanState.getOrbit());
964 for (int i = 0; i < shortPeriodic.length; i++) {
965 sptValue[i] += shortPeriodic[i];
966 }
967 }
968 return sptValue;
969 }
970
971
972 /** Internal mapper using mean parameters plus short periodic terms. */
973 private static class MeanPlusShortPeriodicMapper extends StateMapper {
974
975 /** Short periodic coefficients that must be stored as additional states. */
976 private Set<String> selectedCoefficients;
977
978 /** Number of satellite revolutions in the averaging interval. */
979 private int satelliteRevolution;
980
981 /** Short period terms. */
982 private List<ShortPeriodTerms> shortPeriodTerms;
983
984 /** Simple constructor.
985 * @param referenceDate reference date
986 * @param mu central attraction coefficient (m³/s²)
987 * @param attitudeProvider attitude provider
988 * @param frame inertial frame
989 */
990 MeanPlusShortPeriodicMapper(final AbsoluteDate referenceDate, final double mu,
991 final AttitudeProvider attitudeProvider, final Frame frame) {
992
993 super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, attitudeProvider, frame);
994
995 this.selectedCoefficients = null;
996
997 // Default averaging period for conversion from osculating to mean elements
998 this.satelliteRevolution = 2;
999
1000 this.shortPeriodTerms = Collections.emptyList();
1001
1002 }
1003
1004 /** {@inheritDoc} */
1005 @Override
1006 public SpacecraftState mapArrayToState(final AbsoluteDate date,
1007 final double[] y, final double[] yDot,
1008 final PropagationType type) {
1009
1010 // add short periodic variations to mean elements to get osculating elements
1011 // (the loop may not be performed if there are no force models and in the
1012 // case we want to remain in mean parameters only)
1013 final double[] elements = y.clone();
1014 final DataDictionary coefficients;
1015 if (type == PropagationType.MEAN) {
1016 coefficients = null;
1017 } else {
1018 final Orbit meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame());
1019 coefficients = selectedCoefficients == null ? null : new DataDictionary();
1020 for (final ShortPeriodTerms spt : shortPeriodTerms) {
1021 final double[] shortPeriodic = spt.value(meanOrbit);
1022 for (int i = 0; i < shortPeriodic.length; i++) {
1023 elements[i] += shortPeriodic[i];
1024 }
1025 if (selectedCoefficients != null) {
1026 coefficients.putAllDoubles(spt.getCoefficients(date, selectedCoefficients));
1027 }
1028 }
1029 }
1030
1031 final double mass = elements[6];
1032 if (mass <= 0.0) {
1033 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass);
1034 }
1035
1036 final Orbit orbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame());
1037 final Attitude attitude = getAttitudeProvider().getAttitude(orbit, date, getFrame());
1038
1039 return new SpacecraftState(orbit, attitude, mass, coefficients, null);
1040
1041 }
1042
1043 /** {@inheritDoc} */
1044 @Override
1045 public void mapStateToArray(final SpacecraftState state, final double[] y, final double[] yDot) {
1046
1047 OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.MEAN, y, yDot);
1048 y[6] = state.getMass();
1049
1050 }
1051
1052 /** Set the number of satellite revolutions to use for converting osculating to mean elements.
1053 * <p>
1054 * By default, if the initial orbit is defined as osculating,
1055 * it will be averaged over 2 satellite revolutions.
1056 * This can be changed by using this method.
1057 * </p>
1058 * @param satelliteRevolution number of satellite revolutions to use for converting osculating to mean
1059 * elements
1060 */
1061 public void setSatelliteRevolution(final int satelliteRevolution) {
1062 this.satelliteRevolution = satelliteRevolution;
1063 }
1064
1065 /** Get the number of satellite revolutions to use for converting osculating to mean elements.
1066 * @return number of satellite revolutions to use for converting osculating to mean elements
1067 */
1068 public int getSatelliteRevolution() {
1069 return satelliteRevolution;
1070 }
1071
1072 /** Set the selected short periodic coefficients that must be stored as additional states.
1073 * @param selectedCoefficients short periodic coefficients that must be stored as additional states
1074 * (null means no coefficients are selected, empty set means all coefficients are selected)
1075 */
1076 public void setSelectedCoefficients(final Set<String> selectedCoefficients) {
1077 this.selectedCoefficients = selectedCoefficients;
1078 }
1079
1080 /** Get the selected short periodic coefficients that must be stored as additional states.
1081 * @return short periodic coefficients that must be stored as additional states
1082 * (null means no coefficients are selected, empty set means all coefficients are selected)
1083 */
1084 public Set<String> getSelectedCoefficients() {
1085 return selectedCoefficients;
1086 }
1087
1088 /** Set the short period terms.
1089 * @param shortPeriodTerms short period terms
1090 * @since 7.1
1091 */
1092 public void setShortPeriodTerms(final List<ShortPeriodTerms> shortPeriodTerms) {
1093 this.shortPeriodTerms = shortPeriodTerms;
1094 }
1095
1096 /** Get the short period terms.
1097 * @return shortPeriodTerms short period terms
1098 * @since 7.1
1099 */
1100 public List<ShortPeriodTerms> getShortPeriodTerms() {
1101 return shortPeriodTerms;
1102 }
1103
1104 }
1105
1106 /** {@inheritDoc} */
1107 @Override
1108 protected MainStateEquations getMainStateEquations(final ODEIntegrator integrator) {
1109 return new Main(integrator);
1110 }
1111
1112 /** Internal class for mean parameters integration. */
1113 private class Main implements MainStateEquations {
1114
1115 /** Derivatives array. */
1116 private final double[] yDot;
1117
1118 /** Simple constructor.
1119 * @param integrator numerical integrator to use for propagation.
1120 */
1121 Main(final ODEIntegrator integrator) {
1122 yDot = new double[7];
1123
1124 // Setup event detectors from attitude provider and each force model
1125 getAttitudeProvider().getEventDetectors().forEach(eventDetector -> setUpEventDetector(integrator, eventDetector));
1126 forceModels.forEach(dsstForceModel -> dsstForceModel.getEventDetectors().
1127 forEach(eventDetector -> setUpEventDetector(integrator, eventDetector)));
1128 }
1129
1130 /** {@inheritDoc} */
1131 @Override
1132 public void init(final SpacecraftState initialState, final AbsoluteDate target) {
1133 forceModels.forEach(fm -> fm.init(initialState, target));
1134 }
1135
1136 /** {@inheritDoc} */
1137 @Override
1138 public double[] computeDerivatives(final SpacecraftState state) {
1139
1140 Arrays.fill(yDot, 0.0);
1141
1142 // compute common auxiliary elements
1143 final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(state.getOrbit(), I);
1144
1145 // compute the contributions of all perturbing forces
1146 for (final DSSTForceModel forceModel : forceModels) {
1147 final double[] daidt = elementRates(forceModel, state, auxiliaryElements, forceModel.getParameters(state.getDate()));
1148 for (int i = 0; i < daidt.length; i++) {
1149 yDot[i] += daidt[i];
1150 }
1151 }
1152
1153 return yDot.clone();
1154 }
1155
1156 /** This method allows to compute the mean equinoctial elements rates da<sub>i</sub> / dt
1157 * for a specific force model.
1158 * @param forceModel force to take into account
1159 * @param state current state
1160 * @param auxiliaryElements auxiliary elements related to the current orbit
1161 * @param parameters force model parameters at state date (only 1 value for
1162 * each parameter
1163 * @return the mean equinoctial elements rates da<sub>i</sub> / dt
1164 */
1165 private double[] elementRates(final DSSTForceModel forceModel,
1166 final SpacecraftState state,
1167 final AuxiliaryElements auxiliaryElements,
1168 final double[] parameters) {
1169 return forceModel.getMeanElementRate(state, auxiliaryElements, parameters);
1170 }
1171
1172 }
1173
1174 /** Estimate tolerance vectors for an AdaptativeStepsizeIntegrator.
1175 * <p>
1176 * The errors are estimated from partial derivatives properties of orbits,
1177 * starting from a scalar position error specified by the user.
1178 * Considering the energy conservation equation V = sqrt(mu (2/r - 1/a)),
1179 * we get at constant energy (i.e. on a Keplerian trajectory):
1180 *
1181 * <pre>
1182 * V r² |dV| = mu |dr|
1183 * </pre>
1184 *
1185 * <p> So we deduce a scalar velocity error consistent with the position error. From here, we apply
1186 * orbits Jacobians matrices to get consistent errors on orbital parameters.
1187 *
1188 * <p>
1189 * The tolerances are only <em>orders of magnitude</em>, and integrator tolerances are only
1190 * local estimates, not global ones. So some care must be taken when using these tolerances.
1191 * Setting 1mm as a position error does NOT mean the tolerances will guarantee a 1mm error
1192 * position after several orbits integration.
1193 * </p>
1194 *
1195 * @param dP user specified position error (m)
1196 * @param orbit reference orbit
1197 * @return a two rows array, row 0 being the absolute tolerance error
1198 * and row 1 being the relative tolerance error
1199 * @deprecated since 13.0. Use {@link ToleranceProvider} for default and custom tolerances.
1200 */
1201 @Deprecated
1202 public static double[][] tolerances(final double dP, final Orbit orbit) {
1203 // estimate the scalar velocity error
1204 final PVCoordinates pv = orbit.getPVCoordinates();
1205 final double r2 = pv.getPosition().getNormSq();
1206 final double v = pv.getVelocity().getNorm();
1207 final double dV = orbit.getMu() * dP / (v * r2);
1208
1209 return DSSTPropagator.tolerances(dP, dV, orbit);
1210
1211 }
1212
1213 /** Estimate tolerance vectors for an AdaptativeStepsizeIntegrator.
1214 * <p>
1215 * The errors are estimated from partial derivatives properties of orbits,
1216 * starting from scalar position and velocity errors specified by the user.
1217 * <p>
1218 * The tolerances are only <em>orders of magnitude</em>, and integrator tolerances are only
1219 * local estimates, not global ones. So some care must be taken when using these tolerances.
1220 * Setting 1mm as a position error does NOT mean the tolerances will guarantee a 1mm error
1221 * position after several orbits integration.
1222 * </p>
1223 *
1224 * @param dP user specified position error (m)
1225 * @param dV user specified velocity error (m/s)
1226 * @param orbit reference orbit
1227 * @return a two rows array, row 0 being the absolute tolerance error
1228 * and row 1 being the relative tolerance error
1229 * @since 10.3
1230 * @deprecated since 13.0. Use {@link ToleranceProvider} for default and custom tolerances.
1231 */
1232 @Deprecated
1233 public static double[][] tolerances(final double dP, final double dV, final Orbit orbit) {
1234
1235 return ToleranceProvider.of(CartesianToleranceProvider.of(dP, dV, CartesianToleranceProvider.DEFAULT_ABSOLUTE_MASS_TOLERANCE))
1236 .getTolerances(orbit, OrbitType.EQUINOCTIAL, PositionAngleType.MEAN);
1237 }
1238
1239 /** Step handler used to compute the parameters for the short periodic contributions.
1240 * @author Lucian Barbulescu
1241 */
1242 private class ShortPeriodicsHandler implements ODEStepHandler {
1243
1244 /** Force models used to compute short periodic terms. */
1245 private final List<DSSTForceModel> forceModels;
1246
1247 /** Constructor.
1248 * @param forceModels force models
1249 */
1250 ShortPeriodicsHandler(final List<DSSTForceModel> forceModels) {
1251 this.forceModels = forceModels;
1252 }
1253
1254 /** {@inheritDoc} */
1255 @Override
1256 public void handleStep(final ODEStateInterpolator interpolator) {
1257
1258 // Get the grid points to compute
1259 final double[] interpolationPoints =
1260 interpolationgrid.getGridPoints(interpolator.getPreviousState().getTime(),
1261 interpolator.getCurrentState().getTime());
1262
1263 final SpacecraftState[] meanStates = new SpacecraftState[interpolationPoints.length];
1264 for (int i = 0; i < interpolationPoints.length; ++i) {
1265
1266 // Build the mean state interpolated at grid point
1267 final double time = interpolationPoints[i];
1268 final ODEStateAndDerivative sd = interpolator.getInterpolatedState(time);
1269 meanStates[i] = mapper.mapArrayToState(time,
1270 sd.getPrimaryState(),
1271 sd.getPrimaryDerivative(),
1272 PropagationType.MEAN);
1273 }
1274
1275 // Compute short periodic coefficients for this step
1276 for (DSSTForceModel forceModel : forceModels) {
1277 forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(), meanStates);
1278 }
1279 }
1280 }
1281 }