1 /* Copyright 2002-2024 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.orekit.bodies.GeodeticPoint;
20 import org.orekit.bodies.OneAxisEllipsoid;
21 import org.orekit.frames.Frame;
22 import org.orekit.propagation.PropagatorsParallelizer;
23 import org.orekit.propagation.SpacecraftState;
24 import org.orekit.propagation.events.handlers.ContinueOnEvent;
25 import org.orekit.propagation.events.handlers.EventHandler;
26 import org.orekit.time.AbsoluteDate;
27 import org.orekit.utils.PVCoordinatesProvider;
28
29
30 /** Detector for inter-satellites direct view (i.e. no masking by central body limb).
31 * <p>
32 * As this detector needs two satellites, it embeds one {@link
33 * PVCoordinatesProvider coordinates provider} for the secondary satellite
34 * and is registered as an event detector in the propagator of the primary
35 * satellite. The secondary satellite provider will therefore be driven by this
36 * detector (and hence by the propagator in which this detector is registered).
37 * </p>
38 * <p>
39 * In order to avoid infinite recursion, care must be taken to have the secondary
40 * satellite provider being <em>completely independent</em> from anything else.
41 * In particular, if the provider is a propagator, it should <em>not</em> be run
42 * together in a {@link PropagatorsParallelizer propagators parallelizer} with
43 * the propagator this detector is registered in. It is fine however to configure
44 * two separate propagators PsA and PsB with similar settings for the secondary satellite
45 * and one propagator Pm for the primary satellite and then use Psa in this detector
46 * registered within Pm while Pm and Psb are run in the context of a {@link
47 * PropagatorsParallelizer propagators parallelizer}.
48 * </p>
49 * <p>
50 * For efficiency reason during the event search loop, it is recommended to have
51 * the secondary provider be an analytical propagator or an ephemeris. A numerical propagator
52 * as a secondary propagator works but is expected to be computationally costly.
53 * </p>
54 * <p>
55 * The {@code g} function of this detector is positive when satellites can see
56 * each other directly and negative when the central body limb is in between and
57 * blocks the direct view.
58 * </p>
59 * <p>
60 * This detector only checks masking by central body limb, it does not take into
61 * account satellites antenna patterns. If these patterns must be considered, then
62 * this detector can be {@link BooleanDetector#andCombine(EventDetector...) and combined}
63 * with the {@link BooleanDetector#notCombine(EventDetector) logical not} of
64 * {@link FieldOfViewDetector field of view detectors}.
65 * </p>
66 * @author Luc Maisonobe
67 * @since 9.3
68 */
69 public class InterSatDirectViewDetector extends AbstractDetector<InterSatDirectViewDetector> {
70
71 /** Central body. */
72 private final OneAxisEllipsoid body;
73
74 /** Skimming altitude.
75 * @since 12.0
76 */
77 private final double skimmingAltitude;
78
79 /** Coordinates provider for the secondary satellite. */
80 private final PVCoordinatesProvider secondary;
81
82 /** simple constructor.
83 *
84 * @param body central body
85 * @param secondary provider for the secondary satellite
86 */
87 public InterSatDirectViewDetector(final OneAxisEllipsoid body, final PVCoordinatesProvider secondary) {
88 this(body, 0.0, secondary, AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER,
89 new ContinueOnEvent());
90 }
91
92 /** Private constructor.
93 * @param body central body
94 * @param skimmingAltitude skimming altitude at which events are triggered
95 * @param secondary provider for the secondary satellite
96 * @param maxCheck maximum checking interval
97 * @param threshold convergence threshold (s)
98 * @param maxIter maximum number of iterations in the event time search
99 * @param handler event handler to call at event occurrences
100 * @since 12.0
101 */
102 protected InterSatDirectViewDetector(final OneAxisEllipsoid body,
103 final double skimmingAltitude,
104 final PVCoordinatesProvider secondary,
105 final AdaptableInterval maxCheck,
106 final double threshold,
107 final int maxIter,
108 final EventHandler handler) {
109 super(maxCheck, threshold, maxIter, handler);
110 this.body = body;
111 this.skimmingAltitude = skimmingAltitude;
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 skimming altitude.
123 * @return skimming altitude at which events are triggered
124 * @since 12.0
125 */
126 public double getSkimmingAltitude() {
127 return skimmingAltitude;
128 }
129
130 /** Get the provider for the secondary satellite.
131 * @return provider for the secondary satellite
132 */
133 public PVCoordinatesProvider getSecondary() {
134 return secondary;
135 }
136
137 /** {@inheritDoc} */
138 @Override
139 protected InterSatDirectViewDetector create(final AdaptableInterval newMaxCheck,
140 final double newThreshold,
141 final int newMaxIter,
142 final EventHandler newHandler) {
143 return new InterSatDirectViewDetector(body, skimmingAltitude, secondary,
144 newMaxCheck, newThreshold, newMaxIter, newHandler);
145 }
146
147 /**
148 * Setup the skimming altitude.
149 * <p>
150 * The skimming altitude is the lowest altitude of the path between satellites
151 * at which events should be triggered. If set to 0.0, events are triggered
152 * exactly when the path passes just at central body limb.
153 * </p>
154 * @param newSkimmingAltitude skimming altitude (m)
155 * @return a new detector with updated configuration (the instance is not changed)
156 * @see #getSkimmingAltitude()
157 * @since 12.0
158 */
159 public InterSatDirectViewDetector withSkimmingAltitude(final double newSkimmingAltitude) {
160 return new InterSatDirectViewDetector(body, newSkimmingAltitude, secondary,
161 getMaxCheckInterval(), getThreshold(),
162 getMaxIterationCount(), getHandler());
163 }
164
165 /** {@inheritDoc}
166 * <p>
167 * The {@code g} function of this detector is the difference between the minimum
168 * altitude of intermediate points along the line of sight between satellites and the
169 * {@link #getSkimmingAltitude() skimming altitude}. It is therefore positive when
170 * all intermediate points are above the skimming altitude, meaning satellites can see
171 * each other and it is negative when some intermediate points (which may be either
172 * endpoints) dive below this altitude, meaning satellites cannot see each other.
173 * </p>
174 */
175 @Override
176 public double g(final SpacecraftState state) {
177
178 // get the lowest point between primary and secondary
179 final AbsoluteDate date = state.getDate();
180 final Frame frame = body.getBodyFrame();
181 final GeodeticPoint lowest = body.lowestAltitudeIntermediate(state.getPosition(frame),
182 secondary.getPosition(date, frame));
183
184 // compute switching function value as altitude difference
185 return lowest.getAltitude() - skimmingAltitude;
186
187 }
188
189 }