1 /* Copyright 2002-2025 Joseph Reed
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 * Joseph Reed 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.ode.events.Action;
21 import org.hipparchus.util.MathUtils;
22 import org.orekit.annotation.DefaultDataContext;
23 import org.orekit.bodies.CelestialBodyFactory;
24 import org.orekit.frames.Frame;
25 import org.orekit.frames.FramesFactory;
26 import org.orekit.propagation.SpacecraftState;
27 import org.orekit.propagation.events.handlers.EventHandler;
28 import org.orekit.propagation.events.handlers.StopOnEvent;
29 import org.orekit.utils.PVCoordinatesProvider;
30 import org.orekit.utils.TimeStampedPVCoordinates;
31
32 /** Finder for beta angle crossing events.
33 * <p>Locate events when the beta angle (the angle between the orbit plane and the celestial body)
34 * crosses a threshold. The {@link #g(SpacecraftState)} function is negative when the beta angle
35 * is above the threshold and positive when the beta angle is below the threshold.</p>
36 * <p>The inertial frame provided must have it's origin centered at the satellite's orbit plane. The
37 * beta angle is computed as the angle between the celestial body's position in this frame with the
38 * satellite's orbital momentum vector.</p>
39 * <p>The default implementation behavior is to {@link Action#STOP stop}
40 * propagation at the first event date occurrence. This can be changed by calling
41 * {@link #withHandler(EventHandler)} after construction.</p>
42 * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
43 * @author Joe Reed
44 * @since 12.1
45 */
46 public class BetaAngleDetector extends AbstractDetector<BetaAngleDetector> {
47
48 /** Beta angle crossing threshold. */
49 private final double betaAngleThreshold;
50 /** Coordinate provider for the celestial body. */
51 private final PVCoordinatesProvider celestialBodyProvider;
52 /** Inertial frame in which beta angle is calculated. */
53 private final Frame inertialFrame;
54
55 /**Solar beta angle constructor.
56 * <p>This method uses the default data context, assigns the sun as the celestial
57 * body and uses GCRF as the inertial frame.</p>
58 * @param betaAngleThreshold beta angle threshold (radians)
59 */
60 @DefaultDataContext
61 public BetaAngleDetector(final double betaAngleThreshold) {
62 this(betaAngleThreshold, CelestialBodyFactory.getSun(), FramesFactory.getGCRF());
63 }
64
65 /** Class constructor.
66 * @param betaAngleThreshold beta angle threshold (radians)
67 * @param celestialBodyProvider coordinate provider for the celestial provider
68 * @param inertialFrame inertial frame in which to compute the beta angle
69 */
70 public BetaAngleDetector(final double betaAngleThreshold, final PVCoordinatesProvider celestialBodyProvider,
71 final Frame inertialFrame) {
72 this(EventDetectionSettings.getDefaultEventDetectionSettings(), new StopOnEvent(),
73 betaAngleThreshold, celestialBodyProvider, inertialFrame);
74 }
75
76 /** Protected constructor with full parameters.
77 * <p>This constructor is not public as users are expected to use the builder
78 * API with the various {@code withXxx()} methods to set up the instance
79 * in a readable manner without using a huge amount of parameters.</p>
80 * @param detectionSettings detection settings
81 * @param handler event handler to call at event occurrences
82 * @param betaAngleThreshold beta angle threshold (radians)
83 * @param celestialBodyProvider coordinate provider for the celestial provider
84 * @param inertialFrame inertial frame in which to compute the beta angle
85 */
86 protected BetaAngleDetector(final EventDetectionSettings detectionSettings, final EventHandler handler,
87 final double betaAngleThreshold, final PVCoordinatesProvider celestialBodyProvider,
88 final Frame inertialFrame) {
89 super(detectionSettings, handler);
90 this.betaAngleThreshold = betaAngleThreshold;
91 this.celestialBodyProvider = celestialBodyProvider;
92 this.inertialFrame = inertialFrame;
93 }
94
95 /** Coordinate provider for the celestial body.
96 * @return celestial body's coordinate provider
97 */
98 public PVCoordinatesProvider getCelestialBodyProvider() {
99 return this.celestialBodyProvider;
100 }
101
102 /** The inertial frame in which beta angle is computed.
103 * @return the inertial frame
104 */
105 public Frame getInertialFrame() {
106 return this.inertialFrame;
107 }
108
109 /** The beta angle threshold (radians).
110 * @return the beta angle threshold (radians)
111 */
112 public double getBetaAngleThreshold() {
113 return this.betaAngleThreshold;
114 }
115
116 /** Create a new instance with the provided coordinate provider.
117 * <p>This method does not change the current instance.</p>
118 * @param newProvider the new coordinate provider
119 * @return the new detector instance
120 */
121 public BetaAngleDetector withCelestialProvider(final PVCoordinatesProvider newProvider) {
122 return new BetaAngleDetector(getDetectionSettings(),
123 getHandler(), getBetaAngleThreshold(), newProvider, getInertialFrame());
124 }
125
126 /** Create a new instance with the provided beta angle threshold.
127 * <p>This method does not change the current instance.</p>
128 * @param newBetaAngleThreshold the beta angle threshold (radians)
129 * @return the new detector instance
130 */
131 public BetaAngleDetector withBetaThreshold(final double newBetaAngleThreshold) {
132 return new BetaAngleDetector(getDetectionSettings(), getHandler(),
133 newBetaAngleThreshold, getCelestialBodyProvider(), getInertialFrame());
134 }
135
136 /** Create a new instance with the provided inertial frame.
137 * <p>This method does not change the current instance.</p>
138 * @param newFrame the inertial frame
139 * @return the new detector instance
140 */
141 public BetaAngleDetector withInertialFrame(final Frame newFrame) {
142 return new BetaAngleDetector(getDetectionSettings(),
143 getHandler(), getBetaAngleThreshold(), getCelestialBodyProvider(), newFrame);
144 }
145
146 /** {@inheritDoc} */
147 @Override
148 public double g(final SpacecraftState s) {
149 final double beta = calculateBetaAngle(s, celestialBodyProvider, inertialFrame);
150 return betaAngleThreshold - beta;
151 }
152
153 /**Calculate the beta angle between the orbit plane and the celestial body.
154 * <p>This method computes the beta angle using the frame from the spacecraft state.</p>
155 * @param state spacecraft state
156 * @param celestialBodyProvider celestial body coordinate provider
157 * @return the beta angle (radians)
158 */
159 public static double calculateBetaAngle(final SpacecraftState state,
160 final PVCoordinatesProvider celestialBodyProvider) {
161 return calculateBetaAngle(state, celestialBodyProvider, state.getFrame());
162 }
163
164 /**Calculate the beta angle between the orbit plane and the celestial body.
165 * @param state spacecraft state
166 * @param celestialBodyProvider celestial body coordinate provider
167 * @param frame inertial frame in which beta angle will be computed
168 * @return the beta angle (radians)
169 */
170 public static double calculateBetaAngle(final SpacecraftState state,
171 final PVCoordinatesProvider celestialBodyProvider, final Frame frame) {
172 final Vector3D celestialP = celestialBodyProvider.getPosition(state.getDate(), frame);
173 final TimeStampedPVCoordinates pv = state.getPVCoordinates(frame);
174 return MathUtils.SEMI_PI - Vector3D.angle(celestialP, pv.getMomentum());
175 }
176
177 /** {@inheritDoc} */
178 @Override
179 protected BetaAngleDetector create(final EventDetectionSettings detectionSettings, final EventHandler newHandler) {
180 return new BetaAngleDetector(detectionSettings, newHandler,
181 getBetaAngleThreshold(), getCelestialBodyProvider(), getInertialFrame());
182 }
183 }