1   /* Copyright 2002-2021 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.ode.events.Action;
20  import org.orekit.frames.TopocentricFrame;
21  import org.orekit.models.AtmosphericRefractionModel;
22  import org.orekit.propagation.SpacecraftState;
23  import org.orekit.propagation.events.handlers.EventHandler;
24  import org.orekit.propagation.events.handlers.StopOnDecreasing;
25  import org.orekit.utils.ElevationMask;
26  
27  
28  /**
29   * Finder for satellite raising/setting events that allows for the
30   * setting of azimuth and/or elevation bounds or a ground azimuth/elevation
31   * mask input. Each calculation be configured to use atmospheric refraction
32   * as well.
33   * <p>The default implementation behavior is to {@link Action#CONTINUE continue}
34   * propagation at raising and to {@link Action#STOP stop} propagation
35   * at setting. This can be changed by calling
36   * {@link #withHandler(EventHandler)} after construction.</p>
37   * @author Hank Grabowski
38   * @since 6.1
39   */
40  public class ElevationDetector extends AbstractDetector<ElevationDetector> {
41  
42      /** Elevation mask used for calculations, if defined. */
43      private final ElevationMask elevationMask;
44  
45      /** Minimum elevation value used if mask is not defined. */
46      private final double minElevation;
47  
48      /** Atmospheric Model used for calculations, if defined. */
49      private final AtmosphericRefractionModel refractionModel;
50  
51      /** Topocentric frame in which elevation should be evaluated. */
52      private final TopocentricFrame topo;
53  
54      /**
55       * Creates an instance of Elevation detector based on passed in topocentric frame
56       * and the minimum elevation angle.
57       * <p>
58       * uses default values for maximal checking interval ({@link #DEFAULT_MAXCHECK})
59       * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
60       * @param topo reference to a topocentric model
61       * @see #withConstantElevation(double)
62       * @see #withElevationMask(ElevationMask)
63       * @see #withRefraction(AtmosphericRefractionModel)
64       */
65      public ElevationDetector(final TopocentricFrame topo) {
66          this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, topo);
67      }
68  
69      /**
70       * Creates an instance of Elevation detector based on passed in topocentric frame
71       * and overrides of default maximal checking interval and convergence threshold values.
72       * @param maxCheck maximum checking interval (s)
73       * @param threshold maximum divergence threshold (s)
74       * @param topo reference to a topocentric model
75       * @see #withConstantElevation(double)
76       * @see #withElevationMask(ElevationMask)
77       * @see #withRefraction(AtmosphericRefractionModel)
78       */
79      public ElevationDetector(final double maxCheck, final double threshold,
80                               final TopocentricFrame topo) {
81          this(maxCheck, threshold, DEFAULT_MAX_ITER,
82               new StopOnDecreasing<ElevationDetector>(),
83               0.0, null, null, topo);
84      }
85  
86      /** Private constructor with full parameters.
87       * <p>
88       * This constructor is private as users are expected to use the builder
89       * API with the various {@code withXxx()} methods to set up the instance
90       * in a readable manner without using a huge amount of parameters.
91       * </p>
92       * @param maxCheck maximum checking interval (s)
93       * @param threshold convergence threshold (s)
94       * @param maxIter maximum number of iterations in the event time search
95       * @param handler event handler to call at event occurrences
96       * @param minElevation minimum elevation in radians (rad)
97       * @param mask reference to elevation mask
98       * @param refractionModel reference to refraction model
99       * @param topo reference to a topocentric model
100      */
101     private ElevationDetector(final double maxCheck, final double threshold,
102                               final int maxIter, final EventHandler<? super ElevationDetector> handler,
103                               final double minElevation, final ElevationMask mask,
104                               final AtmosphericRefractionModel refractionModel,
105                               final TopocentricFrame topo) {
106         super(maxCheck, threshold, maxIter, handler);
107         this.minElevation    = minElevation;
108         this.elevationMask   = mask;
109         this.refractionModel = refractionModel;
110         this.topo            = topo;
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     protected ElevationDetector create(final double newMaxCheck, final double newThreshold,
116                                        final int newMaxIter, final EventHandler<? super ElevationDetector> newHandler) {
117         return new ElevationDetector(newMaxCheck, newThreshold, newMaxIter, newHandler,
118                                      minElevation, elevationMask, refractionModel, topo);
119     }
120 
121     /**
122      * Returns the currently configured elevation mask.
123      * @return elevation mask
124      * (null if instance has been configured with {@link #withConstantElevation(double)}
125      * @see #withElevationMask(ElevationMask)
126      */
127     public ElevationMask getElevationMask() {
128         return this.elevationMask;
129     }
130 
131     /**
132      * Returns the currently configured minimum valid elevation value.
133      * @return minimum elevation value
134      * ({@code Double.NaN} if instance has been configured with {@link #withElevationMask(ElevationMask)}
135      * @see #withConstantElevation(double)
136      */
137     public double getMinElevation() {
138         return this.minElevation;
139     }
140 
141     /**
142      * Returns the currently configured refraction model.
143      * @return refraction model
144      * @see #withRefraction(AtmosphericRefractionModel)
145      */
146     public AtmosphericRefractionModel getRefractionModel() {
147         return this.refractionModel;
148     }
149 
150     /**
151      * Returns the currently configured topocentric frame definitions.
152      * @return topocentric frame definition
153      */
154     public TopocentricFrame getTopocentricFrame() {
155         return this.topo;
156     }
157 
158     /** Compute the value of the switching function.
159      * This function measures the difference between the current elevation
160      * (and azimuth if necessary) and the reference mask or minimum value.
161      * @param s the current state information: date, kinematics, attitude
162      * @return value of the switching function
163      */
164     @Override
165     public double g(final SpacecraftState s) {
166 
167         final double trueElevation = topo.getElevation(s.getPVCoordinates().getPosition(),
168                                                        s.getFrame(), s.getDate());
169 
170         final double calculatedElevation;
171         if (refractionModel != null) {
172             calculatedElevation = trueElevation + refractionModel.getRefraction(trueElevation);
173         } else {
174             calculatedElevation = trueElevation;
175         }
176 
177         if (elevationMask != null) {
178             final double azimuth = topo.getAzimuth(s.getPVCoordinates().getPosition(), s.getFrame(), s.getDate());
179             return calculatedElevation - elevationMask.getElevation(azimuth);
180         } else {
181             return calculatedElevation - minElevation;
182         }
183 
184     }
185 
186     /**
187      * Setup the minimum elevation for detection.
188      * <p>
189      * This will override an elevation mask if it has been configured as such previously.
190      * </p>
191      * @param newMinElevation minimum elevation for visibility in radians (rad)
192      * @return a new detector with updated configuration (the instance is not changed)
193      * @see #getMinElevation()
194      * @since 6.1
195      */
196     public ElevationDetector withConstantElevation(final double newMinElevation) {
197         return new ElevationDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
198                                      newMinElevation, null, refractionModel, topo);
199     }
200 
201     /**
202      * Setup the elevation mask for detection using the passed in mask object.
203      * @param newElevationMask elevation mask to use for the computation
204      * @return a new detector with updated configuration (the instance is not changed)
205      * @since 6.1
206      * @see #getElevationMask()
207      */
208     public ElevationDetector withElevationMask(final ElevationMask newElevationMask) {
209         return new ElevationDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
210                                      Double.NaN, newElevationMask, refractionModel, topo);
211     }
212 
213     /**
214      * Setup the elevation detector to use an atmospheric refraction model in its
215      * calculations.
216      * <p>
217      * To disable the refraction when copying an existing elevation
218      * detector, call this method with a null argument.
219      * </p>
220      * @param newRefractionModel refraction model to use for the computation
221      * @return a new detector with updated configuration (the instance is not changed)
222      * @since 6.1
223      * @see #getRefractionModel()
224      */
225     public ElevationDetector withRefraction(final AtmosphericRefractionModel newRefractionModel) {
226         return new ElevationDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
227                                      minElevation, elevationMask, newRefractionModel, topo);
228     }
229 
230 }