1   /* Copyright 2023-2024 Alberto Ferrero
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    * Alberto Ferrero 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.Field;
21  import org.hipparchus.util.FastMath;
22  import org.orekit.bodies.FieldGeodeticPoint;
23  import org.orekit.bodies.OneAxisEllipsoid;
24  import org.orekit.propagation.FieldSpacecraftState;
25  import org.orekit.propagation.events.handlers.FieldEventHandler;
26  import org.orekit.propagation.events.handlers.FieldStopOnIncreasing;
27  
28  
29  /** Detector for geographic latitude crossing.
30   * <p>This detector identifies when a spacecraft crosses a fixed
31   * latitude range with respect to a central body.</p>
32   * @author Alberto Ferrero
33   * @since 12.0
34   * @param <T> type of the field elements
35   */
36  public class FieldLatitudeRangeCrossingDetector <T extends CalculusFieldElement<T>>
37          extends FieldAbstractDetector<FieldLatitudeRangeCrossingDetector<T>, T> {
38  
39      /**
40       * Body on which the latitude is defined.
41       */
42      private final OneAxisEllipsoid body;
43  
44      /**
45       * Fixed latitude to be crossed, lower boundary in radians.
46       */
47      private final double fromLatitude;
48  
49      /**
50       * Fixed latitude to be crossed, upper boundary in radians.
51       */
52      private final double toLatitude;
53  
54      /**
55       * Sign, to get reversed inclusion latitude range (lower > upper).
56       */
57      private final double sign;
58  
59      /**
60       * Build a new detector.
61       * <p>The new instance uses default values for maximal checking interval
62       * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link
63       * #DEFAULT_THRESHOLD}).</p>
64       * @param field        the type of numbers to use.
65       * @param body         body on which the latitude is defined
66       * @param fromLatitude latitude to be crossed, lower range boundary
67       * @param toLatitude   latitude to be crossed, upper range boundary
68       */
69      public FieldLatitudeRangeCrossingDetector(final Field<T> field,
70                                                final OneAxisEllipsoid body,
71                                                final double fromLatitude,
72                                                final double toLatitude) {
73          this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK),
74              field.getZero().add(DEFAULT_THRESHOLD),
75              DEFAULT_MAX_ITER,
76              new FieldStopOnIncreasing<>(),
77              body,
78              fromLatitude,
79              toLatitude);
80      }
81  
82      /**
83       * Build a detector.
84       *
85       * @param maxCheck     maximal checking interval (s)
86       * @param threshold    convergence threshold (s)
87       * @param body         body on which the latitude is defined
88       * @param fromLatitude latitude to be crossed, lower range boundary
89       * @param toLatitude   latitude to be crossed, upper range boundary
90       */
91      public FieldLatitudeRangeCrossingDetector(final T maxCheck, final T threshold,
92                                                final OneAxisEllipsoid body, final double fromLatitude, final double toLatitude) {
93          this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(),
94              body, fromLatitude, toLatitude);
95      }
96  
97      /**
98       * Private constructor with full parameters.
99       * <p>
100      * This constructor is private as users are expected to use the builder
101      * API with the various {@code withXxx()} methods to set up the instance
102      * in a readable manner without using a huge amount of parameters.
103      * </p>
104      *
105      * @param maxCheck     maximum checking interval (s)
106      * @param threshold    convergence threshold (s)
107      * @param maxIter      maximum number of iterations in the event time search
108      * @param handler      event handler to call at event occurrences
109      * @param body         body on which the latitude is defined
110      * @param fromLatitude latitude to be crossed, lower range boundary
111      * @param toLatitude   latitude to be crossed, upper range boundary
112      */
113     protected FieldLatitudeRangeCrossingDetector(final FieldAdaptableInterval<T> maxCheck,
114                                                  final T threshold,
115                                                  final int maxIter,
116                                                  final FieldEventHandler<T> handler,
117                                                  final OneAxisEllipsoid body,
118                                                  final double fromLatitude,
119                                                  final double toLatitude) {
120         super(maxCheck, threshold, maxIter, handler);
121         this.body = body;
122         this.fromLatitude = fromLatitude;
123         this.toLatitude = toLatitude;
124         this.sign = FastMath.signum(toLatitude - fromLatitude);
125     }
126 
127     /**
128      * {@inheritDoc}
129      */
130     @Override
131     protected FieldLatitudeRangeCrossingDetector<T> create(final FieldAdaptableInterval<T> newMaxCheck,
132                                                            final T newThreshold,
133                                                            final int newMaxIter,
134                                                            final FieldEventHandler<T> newHandler) {
135         return new FieldLatitudeRangeCrossingDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler,
136             body, fromLatitude, toLatitude);
137     }
138 
139     /**
140      * Get the body on which the geographic zone is defined.
141      *
142      * @return body on which the geographic zone is defined
143      */
144     public OneAxisEllipsoid getBody() {
145         return body;
146     }
147 
148     /**
149      * Get the fixed latitude range to be crossed (radians), lower boundary.
150      *
151      * @return fixed lower boundary latitude range to be crossed (radians)
152      */
153     public double getFromLatitude() {
154         return fromLatitude;
155     }
156 
157     /**
158      * Get the fixed latitude range to be crossed (radians), upper boundary.
159      *
160      * @return fixed lower boundary latitude range to be crossed (radians)
161      */
162     public double getToLatitude() {
163         return toLatitude;
164     }
165 
166     /**
167      * Compute the value of the detection function.
168      * <p>
169      * The value is positive if the spacecraft latitude is inside the latitude range.
170      * It is positive if the spacecraft is northward to lower boundary range and southward to upper boundary range,
171      * with respect to the fixed latitude range.
172      * </p>
173      *
174      * @param s the current state information: date, kinematics, attitude
175      * @return positive if spacecraft inside the range
176      */
177     public T g(final FieldSpacecraftState<T> s) {
178 
179         // convert state to geodetic coordinates
180         final FieldGeodeticPoint<T> gp = body.transform(s.getPVCoordinates().getPosition(),
181             s.getFrame(), s.getDate());
182 
183         // point latitude
184         final T latitude = gp.getLatitude();
185 
186         // inside or outside latitude range
187         return latitude.subtract(fromLatitude).multiply(latitude.negate().add(toLatitude)).multiply(sign);
188 
189     }
190 
191 }