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