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.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.FastMath;
21  import org.orekit.bodies.OneAxisEllipsoid;
22  import org.orekit.frames.Frame;
23  import org.orekit.propagation.PropagatorsParallelizer;
24  import org.orekit.propagation.SpacecraftState;
25  import org.orekit.propagation.events.handlers.ContinueOnEvent;
26  import org.orekit.propagation.events.handlers.EventHandler;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.utils.PVCoordinatesProvider;
29  
30  
31  /** Detector for inter-satellites direct view (i.e. no masking by central body limb).
32   * <p>
33   * As this detector needs two satellites, it embeds one {@link
34   * PVCoordinatesProvider coordinates provider} for the secondary satellite
35   * and is registered as an event detector in the propagator of the primary
36   * satellite. The secondary satellite provider will therefore be driven by this
37   * detector (and hence by the propagator in which this detector is registered).
38   * </p>
39   * <p>
40   * In order to avoid infinite recursion, care must be taken to have the secondary
41   * satellite provider being <em>completely independent</em> from anything else.
42   * In particular, if the provider is a propagator, it should <em>not</em> be run
43   * together in a {@link PropagatorsParallelizer propagators parallelizer} with
44   * the propagator this detector is registered in. It is fine however to configure
45   * two separate propagators PsA and PsB with similar settings for the secondary satellite
46   * and one propagator Pm for the primary satellite and then use Psa in this detector
47   * registered within Pm while Pm and Psb are run in the context of a {@link
48   * PropagatorsParallelizer propagators parallelizer}.
49   * </p>
50   * <p>
51   * For efficiency reason during the event search loop, it is recommended to have
52   * the secondary provider be an analytical propagator or an ephemeris. A numerical propagator
53   * as a secondary propagator works but is expected to be computationally costly.
54   * </p>
55   * <p>
56   * The {@code g} function of this detector is positive when satellites can see
57   * each other directly and negative when the central body limb is in between and
58   * blocks the direct view.
59   * </p>
60   * <p>
61   * This detector only checks masking by central body limb, it does not take into
62   * account satellites antenna patterns. If these patterns must be considered, then
63   * this detector can be {@link BooleanDetector#andCombine(EventDetector...) and combined}
64   * with  the {@link BooleanDetector#notCombine(EventDetector) logical not} of
65   * {@link FieldOfViewDetector field of view detectors}.
66   * </p>
67   * @author Luc Maisonobe
68   * @since 9.3
69   */
70  public class InterSatDirectViewDetector extends AbstractDetector<InterSatDirectViewDetector> {
71  
72      /** Central body. */
73      private final OneAxisEllipsoid body;
74  
75      /** Equatorial radius squared. */
76      private final double ae2;
77  
78      /** 1 minus flatness squared. */
79      private final double g2;
80  
81      /** Coordinates provider for the secondary satellite. */
82      private final PVCoordinatesProvider secondary;
83  
84      /** simple constructor.
85       *
86       * @param body central body
87       * @param secondary provider for the secondary satellite
88       */
89      public InterSatDirectViewDetector(final OneAxisEllipsoid body, final PVCoordinatesProvider secondary) {
90          this(body, secondary, DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER,
91               new ContinueOnEvent<>());
92      }
93  
94      /** Private constructor.
95       * @param body central body
96       * @param secondary provider for the secondary satellite
97       * @param maxCheck  maximum checking interval (s)
98       * @param threshold convergence threshold (s)
99       * @param maxIter   maximum number of iterations in the event time search
100      * @param handler   event handler to call at event occurrences
101      */
102     private InterSatDirectViewDetector(final OneAxisEllipsoid body,
103                                        final PVCoordinatesProvider secondary,
104                                        final double maxCheck,
105                                        final double threshold,
106                                        final int maxIter,
107                                        final EventHandler<? super InterSatDirectViewDetector> handler) {
108         super(maxCheck, threshold, maxIter, handler);
109         this.body  = body;
110         this.ae2   = body.getEquatorialRadius() * body.getEquatorialRadius();
111         this.g2    = (1.0 - body.getFlattening()) * (1.0 - body.getFlattening());
112         this.secondary = secondary;
113     }
114 
115     /** Get the central body.
116      * @return central body
117      */
118     public OneAxisEllipsoid getCentralBody() {
119         return body;
120     }
121 
122     /** Get the provider for the secondary satellite.
123      * @return provider for the secondary satellite
124      */
125     public PVCoordinatesProvider getSecondary() {
126         return secondary;
127     }
128 
129     /** {@inheritDoc} */
130     @Override
131     protected InterSatDirectViewDetector create(final double newMaxCheck,
132                                                 final double newThreshold,
133                                                 final int newMaxIter,
134                                                 final EventHandler<? super InterSatDirectViewDetector> newHandler) {
135         return new InterSatDirectViewDetector(body, secondary, newMaxCheck, newThreshold, newMaxIter, newHandler);
136     }
137 
138     /** {@inheritDoc}
139      * <p>
140      * The {@code g} function of this detector is positive when satellites can see
141      * each other directly and negative when the central body limb is in between and
142      * blocks the direct view.
143      * </p>
144      */
145     @Override
146     public double g(final SpacecraftState state) {
147 
148         // get the line between primary and secondary in body frame
149         final AbsoluteDate date    = state.getDate();
150         final Frame        frame   = body.getBodyFrame();
151         final Vector3D     pPrimary = state.getPVCoordinates(frame).getPosition();
152         final Vector3D     pSecondary  = secondary.getPVCoordinates(date, frame).getPosition();
153 
154         // points along the primary/secondary lines are defined as
155         // xk = x + k * dx, yk = y + k * dy, zk = z + k * dz
156         // so k is 0 at primary and 1 at secondary
157         final double x  = pPrimary.getX();
158         final double y  = pPrimary.getY();
159         final double z  = pPrimary.getZ();
160         final double dx = pSecondary.getX() - x;
161         final double dy = pSecondary.getY() - y;
162         final double dz = pSecondary.getZ() - z;
163 
164         // intersection between line and central body surface
165         // is a root of a 2nd degree polynomial :
166         // a k^2 - 2 b k + c = 0
167         final double a =   g2 * (dx * dx + dy * dy) + dz * dz;
168         final double b = -(g2 * (x * dx + y * dy) + z * dz);
169         final double c =   g2 * (x * x + y * y - ae2) + z * z;
170         final double s = b * b - a * c;
171         if (s < 0) {
172             // the quadratic has no solution, the line between primary and secondary
173             // doesn't crosses central body limb, direct view is possible
174             // return a positive value, preserving continuity across zero crossing
175             return -s;
176         }
177 
178         // the quadratic has two solutions (degenerated to one if s = 0)
179         // direct view is blocked when one of these solutions is between 0 and 1
180         final double k1 = (b < 0) ? (b - FastMath.sqrt(s)) / a : c / (b + FastMath.sqrt(s));
181         final double k2 = c / (a * k1);
182         if (FastMath.max(k1, k2) < 0.0 || FastMath.min(k1, k2) > 1.0) {
183             // the intersections are either behind primary or farther away than secondary
184             // along the line, direct view is possible
185             // return a positive value, preserving continuity across zero crossing
186             return s;
187         } else {
188             // part of the central body is between primary and secondary
189             // this includes unrealistic cases where primary, secondary or both are inside the central body ;-)
190             // in all these cases, direct view is blocked
191             // return a negative value, preserving continuity across zero crossing
192             return -s;
193         }
194 
195     }
196 
197 }