1   /* Copyright 2002-2026 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.events;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.ode.events.Action;
21  import org.orekit.bodies.BodyShape;
22  import org.orekit.propagation.FieldSpacecraftState;
23  import org.orekit.propagation.events.functions.AltitudeEventFunction;
24  import org.orekit.propagation.events.functions.EventFunctionModifier;
25  import org.orekit.propagation.events.handlers.EventHandler;
26  import org.orekit.propagation.events.handlers.FieldEventHandler;
27  import org.orekit.propagation.events.handlers.FieldStopOnDecreasing;
28  
29  /** Finder for satellite altitude crossing events.
30   * <p>This class finds altitude events (i.e. satellite crossing
31   * a predefined altitude level above ground).</p>
32   * <p>The default implementation behavior is to {@link Action#CONTINUE
33   * continue} propagation when ascending and to {@link Action#STOP stop}
34   * propagation when descending. This can be changed by calling
35   * {@link #withHandler(FieldEventHandler)} after construction.</p>
36   * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector)
37   * @author Luc Maisonobe
38   * @since 9.0
39   * @param <T> type of the field elements
40   */
41  public class FieldAltitudeDetector<T extends CalculusFieldElement<T>> extends FieldAbstractGeographicalDetector<FieldAltitudeDetector<T>, T> {
42  
43      /** Threshold altitude value (m). */
44      private final T altitude;
45  
46      /** Build a new altitude detector.
47       * <p>This simple constructor takes default values for maximal checking
48       *  interval ({@link #DEFAULT_MAX_CHECK}) and convergence threshold
49       * ({@link #DEFAULT_THRESHOLD}).</p>
50       * @param altitude threshold altitude value
51       * @param bodyShape body shape with respect to which altitude should be evaluated
52       */
53      public FieldAltitudeDetector(final T altitude, final BodyShape bodyShape) {
54          this(altitude.getField().getZero().newInstance(DEFAULT_MAX_CHECK),
55               altitude.getField().getZero().newInstance(DEFAULT_THRESHOLD),
56               altitude, bodyShape);
57      }
58  
59      /** Build a new altitude detector.
60       * <p>This simple constructor takes default value for convergence threshold
61       * ({@link #DEFAULT_THRESHOLD}).</p>
62       * <p>The maximal interval between altitude checks should
63       * be smaller than the half duration of the minimal pass to handle,
64       * otherwise some short passes could be missed.</p>
65       * @param maxCheck maximal checking interval (s)
66       * @param altitude threshold altitude value (m)
67       * @param bodyShape body shape with respect to which altitude should be evaluated
68       */
69      public FieldAltitudeDetector(final T maxCheck,
70                                   final T altitude,
71                                   final BodyShape bodyShape) {
72          this(maxCheck, altitude.getField().getZero().newInstance(DEFAULT_THRESHOLD), altitude, bodyShape);
73      }
74  
75      /** Build a new altitude detector.
76       * <p>The maximal interval between altitude checks should
77       * be smaller than the half duration of the minimal pass to handle,
78       * otherwise some short passes could be missed.</p>
79       * <p>The maximal interval between altitude checks should
80       * be smaller than the half duration of the minimal pass to handle,
81       * otherwise some short passes could be missed.</p>
82       * @param maxCheck maximal checking interval (s)
83       * @param threshold convergence threshold (s)
84       * @param altitude threshold altitude value (m)
85       * @param bodyShape body shape with respect to which altitude should be evaluated
86       */
87      public FieldAltitudeDetector(final T maxCheck,
88                                   final T threshold,
89                                   final T altitude,
90                                   final BodyShape bodyShape) {
91          this(new FieldEventDetectionSettings<>(maxCheck.getReal(), threshold, DEFAULT_MAX_ITER), new FieldStopOnDecreasing<>(),
92               altitude, bodyShape);
93      }
94  
95      /** Protected constructor with full parameters.
96       * <p>
97       * This constructor is not public as users are expected to use the builder
98       * API with the various {@code withXxx()} methods to set up the instance
99       * in a readable manner without using a huge amount of parameters.
100      * </p>
101      * @param detectionSettings event detection settings
102      * @param handler event handler to call at event occurrences
103      * @param altitude threshold altitude value (m)
104      * @param bodyShape body shape with respect to which altitude should be evaluated
105      * @since 13.0
106      */
107     protected FieldAltitudeDetector(final FieldEventDetectionSettings<T> detectionSettings,
108                                     final FieldEventHandler<T> handler, final T altitude, final BodyShape bodyShape) {
109         super(EventFunctionModifier.addFieldValue(new AltitudeEventFunction(bodyShape, altitude.getReal()),
110                 altitude.getAddendum()), detectionSettings, handler, bodyShape);
111         this.altitude  = altitude;
112     }
113 
114     /** {@inheritDoc} */
115     @Override
116     protected FieldAltitudeDetector<T> create(final FieldEventDetectionSettings<T> detectionSettings,
117                                               final FieldEventHandler<T> newHandler) {
118         return new FieldAltitudeDetector<>(detectionSettings, newHandler, altitude, getBodyShape());
119     }
120 
121     /** Get the threshold altitude value.
122      * @return the threshold altitude value (m)
123      */
124     public T getAltitude() {
125         return altitude;
126     }
127 
128     /** Compute the value of the switching function.
129      * This function measures the difference between the current altitude
130      * and the threshold altitude.
131      * @param s the current state information: date, kinematics, attitude
132      * @return value of the switching function
133      */
134     public T g(final FieldSpacecraftState<T> s) {
135         return getEventFunction().value(s);
136     }
137 
138     /** {@inheritDoc} */
139     @Override
140     public AltitudeDetector toEventDetector(final EventHandler eventHandler) {
141         return new AltitudeDetector(altitude.getReal(), getBodyShape()).withHandler(eventHandler);
142     }
143 }