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  
18  package org.orekit.propagation.events;
19  
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.Field;
22  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23  import org.hipparchus.ode.events.Action;
24  import org.orekit.propagation.FieldSpacecraftState;
25  import org.orekit.propagation.PropagatorsParallelizer;
26  import org.orekit.propagation.events.handlers.EventHandler;
27  import org.orekit.propagation.events.handlers.FieldEventHandler;
28  import org.orekit.propagation.events.handlers.FieldStopOnIncreasing;
29  import org.orekit.utils.FieldPVCoordinates;
30  import org.orekit.utils.FieldPVCoordinatesProvider;
31  import org.orekit.utils.PVCoordinatesProvider;
32  import org.orekit.utils.TimeStampedFieldPVCoordinates;
33  import org.orekit.utils.TimeStampedPVCoordinates;
34  
35  /**
36   * Finder for extremum approach events.
37   * <p>
38   * This class finds extremum approach events (i.e. closest or farthest approach).
39   * </p>
40   * <p>
41   * The default implementation behavior is to {@link Action#CONTINUE continue} propagation at farthest approach and to
42   * {@link Action#STOP stop} propagation at closest approach. This can be changed by calling
43   * {@link FieldAbstractDetector#withHandler(FieldEventHandler)} after construction (go to the end of the documentation to see
44   * an example).
45   * </p>
46   * <p>
47   * As this detector needs two objects (moving relative to each other), it embeds one
48   * {@link FieldPVCoordinatesProvider fielded coordinates provider} for the secondary object and is registered as an event
49   * detector in the propagator of the primary object. The secondary object
50   * {@link FieldPVCoordinatesProvider fielded coordinates provider} will therefore be driven by this detector (and hence by
51   * the propagator in which this detector is registered). Note that you can also create this detector using a standard
52   * {@link PVCoordinatesProvider coordinates provider}
53   * </p>
54   * <p><b>
55   * In order to avoid infinite recursion, care must be taken to have the secondary object provider being <em>completely
56   * independent</em> from anything else. In particular, if the provider is a propagator, it should <em>not</em> be run
57   * together in a {@link PropagatorsParallelizer propagators parallelizer} with the propagator this detector is registered in.
58   * It is fine however to configure two separate propagators PsA and PsB with similar settings for the secondary object and
59   * one propagator Pm for the primary object and then use Psa in this detector registered within Pm while Pm and Psb are run
60   * in the context of a {@link PropagatorsParallelizer propagators parallelizer}.
61   * </b></p>
62   * <p>
63   * For efficiency reason during the event search loop, it is recommended to have the secondary provider be an analytical
64   * propagator or an ephemeris. A numerical propagator as a secondary propagator works but is expected to be computationally
65   * costly.
66   * </p>
67   * <p>
68   * Also, it is possible to detect solely one type of event using an {@link EventSlopeFilter event slope filter}. For example
69   * in order to only detect closest approach, one should type the following :
70   * </p>
71   * <pre>{@code
72   * FieldExtremumApproachDetector<Type> extremumApproachDetector = new FieldExtremumApproachDetector<>(field, secondaryPVProvider);
73   * FieldEventDetector<Type> closeApproachDetector = new FieldEventSlopeFilter<>(extremumApproachDetector, FilterType.TRIGGER_ONLY_INCREASING_EVENTS);
74   *  }
75   * </pre>
76   *
77   * @author Vincent Cucchietti
78   * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector)
79   * @see FieldEventSlopeFilter
80   * @see FilterType
81   * @since 11.3
82   */
83  public class FieldExtremumApproachDetector<T extends CalculusFieldElement<T>>
84          extends FieldAbstractDetector<FieldExtremumApproachDetector<T>, T> {
85  
86      /**
87       * PVCoordinates provider of the other object with which we want to find out the extremum approach.
88       */
89      private final FieldPVCoordinatesProvider<T> secondaryPVProvider;
90  
91      /**
92       * Constructor with default values.
93       * <p>
94       * By default, the implemented behavior is to {@link Action#CONTINUE continue} propagation at farthest approach and to
95       * {@link Action#STOP stop} propagation at closest approach.
96       * <p>
97       * <b>BEWARE : This constructor will "fieldify" given secondary PV coordinates provider.</b>
98       *
99       * @param field field the type of number to use
100      * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum
101      * approach.
102      */
103     public FieldExtremumApproachDetector(final Field<T> field, final PVCoordinatesProvider secondaryPVProvider) {
104         this(field, (FieldPVCoordinatesProvider<T>) (date, frame) -> {
105             final TimeStampedPVCoordinates timeStampedPV =
106                     secondaryPVProvider.getPVCoordinates(date.toAbsoluteDate(), frame);
107             return new TimeStampedFieldPVCoordinates<>(field, timeStampedPV);
108         });
109     }
110 
111     /**
112      * Constructor with default values.
113      * <p>
114      * By default, the implemented behavior is to {@link Action#CONTINUE continue} propagation at farthest approach and to
115      * {@link Action#STOP stop} propagation at closest approach.
116      * </p>
117      *
118      * @param field field the type of number to use
119      * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum
120      * approach.
121      */
122     public FieldExtremumApproachDetector(final Field<T> field, final FieldPVCoordinatesProvider<T> secondaryPVProvider) {
123         this(new FieldEventDetectionSettings<>(field, EventDetectionSettings.getDefaultEventDetectionSettings()),
124              new FieldStopOnIncreasing<>(), secondaryPVProvider);
125     }
126 
127     /**
128      * Constructor.
129      * <p>
130      * This constructor is to be used if the user wants to change the default behavior of the detector.
131      * </p>
132      *
133      * @param detectionSettings Event detection settings.
134      * @param handler Event handler to call at event occurrences.
135      * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum
136      * approach.
137      * @since 13.0
138      * @see EventHandler
139      */
140     protected FieldExtremumApproachDetector(final FieldEventDetectionSettings<T> detectionSettings,
141                                             final FieldEventHandler<T> handler,
142                                             final FieldPVCoordinatesProvider<T> secondaryPVProvider) {
143         super(detectionSettings, handler);
144         this.secondaryPVProvider = secondaryPVProvider;
145     }
146 
147     /**
148      * Compute the relative PV between primary and secondary objects.
149      *
150      * @param s Spacecraft state.
151      *
152      * @return Relative position between primary (=s) and secondaryPVProvider.
153      */
154     public FieldPVCoordinates<T> computeDeltaPV(final FieldSpacecraftState<T> s) {
155         final FieldVector3D<T> primaryPos = s.getPosition();
156         final FieldVector3D<T> primaryVel = s.getPVCoordinates().getVelocity();
157 
158         final FieldPVCoordinates<T> secondaryPV  = secondaryPVProvider.getPVCoordinates(s.getDate(), s.getFrame());
159         final FieldVector3D<T>      secondaryPos = secondaryPV.getPosition();
160         final FieldVector3D<T>      secondaryVel = secondaryPV.getVelocity();
161 
162         final FieldVector3D<T> relativePos = secondaryPos.subtract(primaryPos);
163         final FieldVector3D<T> relativeVel = secondaryVel.subtract(primaryVel);
164 
165         return new FieldPVCoordinates<>(relativePos, relativeVel);
166     }
167 
168     /**
169      * Get the secondary position-velocity provider stored in this instance.
170      *
171      * @return the secondary position-velocity provider stored in this instance
172      */
173     public FieldPVCoordinatesProvider<T> getSecondaryPVProvider() {
174         return secondaryPVProvider;
175     }
176 
177     /**
178      * The {@code g} is positive when the primary object is getting further away from the secondary object and is negative
179      * when it is getting closer to it.
180      *
181      * @param s the current state information: date, kinematics, attitude
182      *
183      * @return value of the switching function
184      */
185     @Override
186     public T g(final FieldSpacecraftState<T> s) {
187         final FieldPVCoordinates<T> deltaPV = computeDeltaPV(s);
188         return FieldVector3D.dotProduct(deltaPV.getPosition(), deltaPV.getVelocity());
189     }
190 
191     /** {@inheritDoc} */
192     @Override
193     protected FieldExtremumApproachDetector<T> create(final FieldEventDetectionSettings<T> detectionSettings,
194                                                       final FieldEventHandler<T> newHandler) {
195         return new FieldExtremumApproachDetector<>(detectionSettings, newHandler, secondaryPVProvider);
196     }
197 }