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.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, EventDetectionSettings.getDefaultEventDetectionSettings(),
89 new ContinueOnEvent());
90 }
91
92 /** Protected 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 detectionSettings detection settings
97 * @param handler event handler to call at event occurrences
98 * @since 13.0
99 */
100 protected InterSatDirectViewDetector(final OneAxisEllipsoid body,
101 final double skimmingAltitude,
102 final PVCoordinatesProvider secondary,
103 final EventDetectionSettings detectionSettings,
104 final EventHandler handler) {
105 super(detectionSettings, handler);
106 this.body = body;
107 this.skimmingAltitude = skimmingAltitude;
108 this.secondary = secondary;
109 }
110
111 /** Get the central body.
112 * @return central body
113 */
114 public OneAxisEllipsoid getCentralBody() {
115 return body;
116 }
117
118 /** Get the skimming altitude.
119 * @return skimming altitude at which events are triggered
120 * @since 12.0
121 */
122 public double getSkimmingAltitude() {
123 return skimmingAltitude;
124 }
125
126 /** Get the provider for the secondary satellite.
127 * @return provider for the secondary satellite
128 */
129 public PVCoordinatesProvider getSecondary() {
130 return secondary;
131 }
132
133 /** {@inheritDoc} */
134 @Override
135 protected InterSatDirectViewDetector create(final EventDetectionSettings detectionSettings,
136 final EventHandler newHandler) {
137 return new InterSatDirectViewDetector(body, skimmingAltitude, secondary,
138 detectionSettings, newHandler);
139 }
140
141 /**
142 * Setup the skimming altitude.
143 * <p>
144 * The skimming altitude is the lowest altitude of the path between satellites
145 * at which events should be triggered. If set to 0.0, events are triggered
146 * exactly when the path passes just at central body limb.
147 * </p>
148 * @param newSkimmingAltitude skimming altitude (m)
149 * @return a new detector with updated configuration (the instance is not changed)
150 * @see #getSkimmingAltitude()
151 * @since 12.0
152 */
153 public InterSatDirectViewDetector withSkimmingAltitude(final double newSkimmingAltitude) {
154 return new InterSatDirectViewDetector(body, newSkimmingAltitude, secondary,
155 getDetectionSettings(), getHandler());
156 }
157
158 /** {@inheritDoc}
159 * <p>
160 * The {@code g} function of this detector is the difference between the minimum
161 * altitude of intermediate points along the line of sight between satellites and the
162 * {@link #getSkimmingAltitude() skimming altitude}. It is therefore positive when
163 * all intermediate points are above the skimming altitude, meaning satellites can see
164 * each other and it is negative when some intermediate points (which may be either
165 * endpoints) dive below this altitude, meaning satellites cannot see each other.
166 * </p>
167 */
168 @Override
169 public double g(final SpacecraftState state) {
170
171 // get the lowest point between primary and secondary
172 final AbsoluteDate date = state.getDate();
173 final Frame frame = body.getBodyFrame();
174 final GeodeticPoint lowest = body.lowestAltitudeIntermediate(state.getPosition(frame),
175 secondary.getPosition(date, frame));
176
177 // compute switching function value as altitude difference
178 return lowest.getAltitude() - skimmingAltitude;
179
180 }
181
182 }