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
18 package org.orekit.propagation.events;
19
20 import org.orekit.annotation.DefaultDataContext;
21 import org.orekit.bodies.GeodeticPoint;
22 import org.orekit.bodies.OneAxisEllipsoid;
23 import org.orekit.data.DataContext;
24 import org.orekit.errors.OrekitIllegalArgumentException;
25 import org.orekit.models.earth.GeoMagneticField;
26 import org.orekit.models.earth.GeoMagneticFieldFactory.FieldModel;
27 import org.orekit.orbits.OrbitType;
28 import org.orekit.propagation.SpacecraftState;
29 import org.orekit.propagation.events.handlers.EventHandler;
30 import org.orekit.propagation.events.handlers.StopOnIncreasing;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.TimeScale;
33
34 /** Detector for South-Atlantic anomaly frontier crossing.
35 * <p>
36 * The detector is based on the value of the earth magnetic field at see level at the satellite latitude and longitude.
37 * </p>
38 * @author Romaric Her
39 */
40 public class MagneticFieldDetector extends AbstractDetector<MagneticFieldDetector> {
41
42 /** Fixed threshold value of Magnetic field to be crossed. */
43 private final double limit;
44
45 /** Fixed altitude of computed magnetic field value. */
46 private final boolean seaLevel;
47
48 /** earth geomagnetic field. */
49 private GeoMagneticField field;
50
51 /** year of the current state. */
52 private double currentYear;
53
54 /** the geomagnetic field model enum. */
55 private final FieldModel type;
56
57 /** the body. */
58 private final OneAxisEllipsoid body;
59
60 /** the timescale. */
61 private final DataContext dataContext;
62
63
64 /** Build a new detector.
65 * <p>The new instance uses default values for maximal checking interval
66 * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link
67 * #DEFAULT_THRESHOLD}).</p>
68 *
69 * <p>This method uses the {@link DataContext#getDefault() default data context}.
70 *
71 * @param limit the threshold value of magnetic field at see level
72 * @param type the magnetic field model
73 * @param body the body
74 * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN}
75 * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext)
76 */
77 @DefaultDataContext
78 public MagneticFieldDetector(final double limit, final FieldModel type, final OneAxisEllipsoid body)
79 throws OrekitIllegalArgumentException {
80 this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, type, body, false);
81 }
82
83 /** Build a new detector.
84 * <p>The new instance uses default values for maximal checking interval
85 * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link
86 * #DEFAULT_THRESHOLD}).</p>
87 *
88 * <p>This method uses the {@link DataContext#getDefault() default data context}.
89 *
90 * @param limit the threshold value of magnetic field at see level
91 * @param type the magnetic field model
92 * @param body the body
93 * @param seaLevel true if the magnetic field intensity is computed at the sea level, false if it is computed at satellite altitude
94 * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN}
95 * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext)
96 */
97 @DefaultDataContext
98 public MagneticFieldDetector(final double limit, final FieldModel type, final OneAxisEllipsoid body, final boolean seaLevel)
99 throws OrekitIllegalArgumentException {
100 this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, type, body, seaLevel);
101 }
102
103 /** Build a detector.
104 *
105 * <p>This method uses the {@link DataContext#getDefault() default data context}.
106 *
107 * @param maxCheck maximal checking interval (s)
108 * @param threshold convergence threshold (s)
109 * @param limit the threshold value of magnetic field at see level
110 * @param type the magnetic field model
111 * @param body the body
112 * @param seaLevel true if the magnetic field intensity is computed at the sea level, false if it is computed at satellite altitude
113 * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN}
114 * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext)
115 */
116 @DefaultDataContext
117 public MagneticFieldDetector(final double maxCheck, final double threshold, final double limit,
118 final FieldModel type, final OneAxisEllipsoid body, final boolean seaLevel)
119 throws OrekitIllegalArgumentException {
120 this(maxCheck, threshold, limit, type, body, seaLevel,
121 DataContext.getDefault());
122 }
123
124 /**
125 * Build a detector.
126 *
127 * @param maxCheck maximal checking interval (s)
128 * @param threshold convergence threshold (s)
129 * @param limit the threshold value of magnetic field at see level
130 * @param type the magnetic field model
131 * @param body the body
132 * @param seaLevel true if the magnetic field intensity is computed at the sea
133 * level, false if it is computed at satellite altitude
134 * @param dataContext used to look up the magnetic field model.
135 * @throws OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN}
136 * @since 10.1
137 */
138 public MagneticFieldDetector(final double maxCheck,
139 final double threshold,
140 final double limit,
141 final FieldModel type,
142 final OneAxisEllipsoid body,
143 final boolean seaLevel,
144 final DataContext dataContext)
145 throws OrekitIllegalArgumentException {
146 this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing<>(),
147 limit, type, body, seaLevel, dataContext);
148 }
149
150 /** Private constructor with full parameters.
151 * <p>
152 * This constructor is private as users are expected to use the builder
153 * API with the various {@code withXxx()} methods to set up the instance
154 * in a readable manner without using a huge amount of parameters.
155 * </p>
156 * @param maxCheck maximum checking interval (s)
157 * @param threshold convergence threshold (s)
158 * @param maxIter maximum number of iterations in the event time search
159 * @param handler event handler to call at event occurrences
160 * @param limit the threshold value of magnetic field at see level
161 * @param type the magnetic field model
162 * @param body the body
163 * @param seaLevel true if the magnetic field intensity is computed at the sea level, false if it is computed at satellite altitude
164 * @param dataContext used to look up the magnetic field model.
165 * @exception OrekitIllegalArgumentException if orbit type is {@link OrbitType#CARTESIAN}
166 */
167 private MagneticFieldDetector(final double maxCheck, final double threshold,
168 final int maxIter, final EventHandler<? super MagneticFieldDetector> handler,
169 final double limit, final FieldModel type, final OneAxisEllipsoid body, final boolean seaLevel,
170 final DataContext dataContext)
171 throws OrekitIllegalArgumentException {
172
173 super(maxCheck, threshold, maxIter, handler);
174
175 this.limit = limit;
176 this.type = type;
177 this.body = body;
178 this.seaLevel = seaLevel;
179 this.dataContext = dataContext;
180 }
181
182 /** {@inheritDoc} */
183 @Override
184 protected MagneticFieldDetector create(final double newMaxCheck, final double newThreshold,
185 final int newMaxIter, final EventHandler<? super MagneticFieldDetector> newHandler) {
186 return new MagneticFieldDetector(newMaxCheck, newThreshold, newMaxIter, newHandler,
187 limit, type, body, seaLevel, dataContext);
188 }
189
190 /** {@inheritDoc} */
191 public void init(final SpacecraftState s0, final AbsoluteDate t) {
192 super.init(s0, t);
193 final TimeScale utc = dataContext.getTimeScales().getUTC();
194 this.currentYear = s0.getDate().getComponents(utc).getDate().getYear();
195 this.field = dataContext.getGeoMagneticFields().getField(type, currentYear);
196 }
197
198 /** Compute the value of the detection function.
199 * <p>
200 * The value is the angle difference between the spacecraft and the fixed
201 * angle to be crossed, with some sign tweaks to ensure continuity.
202 * These tweaks imply the {@code increasing} flag in events detection becomes
203 * irrelevant here! As an example, the angle always increase in a Keplerian
204 * orbit, but this g function will increase and decrease so it
205 * will cross the zero value once per orbit, in increasing and decreasing
206 * directions on alternate orbits..
207 * </p>
208 * @param s the current state information: date, kinematics, attitude
209 * @return angle difference between the spacecraft and the fixed
210 * angle, with some sign tweaks to ensure continuity
211 */
212 public double g(final SpacecraftState s) {
213 final TimeScale utc = dataContext.getTimeScales().getUTC();
214 if (s.getDate().getComponents(utc).getDate().getYear() != currentYear) {
215 this.currentYear = s.getDate().getComponents(utc).getDate().getYear();
216 this.field = dataContext.getGeoMagneticFields().getField(type, currentYear);
217 }
218 final GeodeticPoint geoPoint = body.transform(s.getPVCoordinates().getPosition(), s.getFrame(), s.getDate());
219 final double altitude;
220 if (seaLevel) {
221 altitude = 0;
222 }
223 else {
224 altitude = geoPoint.getAltitude();
225 }
226 final double value = field.calculateField(geoPoint.getLatitude(), geoPoint.getLongitude(), altitude).getTotalIntensity();
227 return value - limit;
228 }
229
230 }