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 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.models.earth.GeoMagneticField; 25 import org.orekit.models.earth.GeoMagneticFieldFactory; 26 import org.orekit.models.earth.GeoMagneticFieldFactory.FieldModel; 27 import org.orekit.propagation.SpacecraftState; 28 import org.orekit.propagation.events.handlers.EventHandler; 29 import org.orekit.propagation.events.handlers.StopOnIncreasing; 30 import org.orekit.time.AbsoluteDate; 31 import org.orekit.time.TimeScale; 32 33 /** Detector for Earth magnetic field strength. 34 * <p> 35 * The detector is based on the field intensity calculated at the 36 * satellite's latitude and longitude, either at sea level or at 37 * satellite altitude, depending on the value chosen for the 38 * <code>atSeaLevel</code> indicator.<br> 39 * It can detect flyovers of the South-Atlantic anomaly with 40 * a classically accepted limit value of 32,000 nT at sea level. 41 * </p> 42 * @author Romaric Her 43 */ 44 public class MagneticFieldDetector extends AbstractDetector<MagneticFieldDetector> { 45 46 /** Fixed threshold value of Magnetic field to be crossed, in Teslas. */ 47 private final double limit; 48 49 /** Switch for calculating field strength at sea level (true) or satellite altitude (false). */ 50 private final boolean atSeaLevel; 51 52 /** Earth geomagnetic field. */ 53 private GeoMagneticField field; 54 55 /** year of the current state. */ 56 private double currentYear; 57 58 /** Earth geomagnetic field model. */ 59 private final FieldModel model; 60 61 /** Earth body shape. */ 62 private final OneAxisEllipsoid body; 63 64 /** Current data context. */ 65 private final DataContext dataContext; 66 67 68 /** Build a new detector. 69 * 70 * <p>This constructor uses: 71 * <ul> 72 * <li>the {@link DataContext#getDefault() default data context}</li> 73 * <li>the {@link AbstractDetector#DEFAULT_MAXCHECK default value} for maximal checking interval</li> 74 * <li>the {@link AbstractDetector#DEFAULT_THRESHOLD default value} for convergence threshold</li> 75 * <li>the <code>atSeaLevel</code> switch set to false</li> 76 * </ul> 77 * 78 * @param limit threshold value for magnetic field detection, in Teslas 79 * @param model magnetic field model 80 * @param body Earth body shape 81 * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext) 82 */ 83 @DefaultDataContext 84 public MagneticFieldDetector(final double limit, final FieldModel model, final OneAxisEllipsoid body) { 85 this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, model, body, false); 86 } 87 88 /** Build a new detector. 89 * 90 * <p>This constructor uses: 91 * <ul> 92 * <li>the {@link DataContext#getDefault() default data context}</li> 93 * <li>the {@link AbstractDetector#DEFAULT_MAXCHECK default value} for maximal checking interval</li> 94 * <li>the {@link AbstractDetector#DEFAULT_THRESHOLD default value} for convergence threshold </li> 95 * </ul> 96 * 97 * @param limit threshold value for magnetic field detection, in Teslas 98 * @param model magnetic field model 99 * @param body Earth body shape 100 * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) 101 * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext) 102 */ 103 @DefaultDataContext 104 public MagneticFieldDetector(final double limit, final FieldModel model, 105 final OneAxisEllipsoid body, final boolean atSeaLevel) { 106 this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, limit, model, body, atSeaLevel); 107 } 108 109 /** Build a detector. 110 * 111 * <p>This method uses the {@link DataContext#getDefault() default data context}.</p> 112 * 113 * @param maxCheck maximal checking interval (s) 114 * @param threshold convergence threshold (s) 115 * @param limit threshold value for magnetic field detection, in Teslas 116 * @param model magnetic field model 117 * @param body Earth body shape 118 * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) 119 * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext) 120 */ 121 @DefaultDataContext 122 public MagneticFieldDetector(final double maxCheck, final double threshold, final double limit, 123 final FieldModel model, final OneAxisEllipsoid body, final boolean atSeaLevel) { 124 this(maxCheck, threshold, limit, model, body, atSeaLevel, DataContext.getDefault()); 125 } 126 127 /** 128 * Build a detector. 129 * 130 * @param maxCheck maximal checking interval (s) 131 * @param threshold convergence threshold (s) 132 * @param limit threshold value for magnetic field detection, in Teslas 133 * @param model magnetic field model 134 * @param body Earth body shape 135 * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) 136 * @param dataContext used to look up the magnetic field model. 137 * @since 10.1 138 */ 139 public MagneticFieldDetector(final double maxCheck, 140 final double threshold, 141 final double limit, 142 final FieldModel model, 143 final OneAxisEllipsoid body, 144 final boolean atSeaLevel, 145 final DataContext dataContext) { 146 this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), 147 limit, model, body, atSeaLevel, dataContext); 148 } 149 150 /** Protected constructor with full parameters. 151 * <p> 152 * This constructor is not public 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 maximal checking interval 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 threshold value for magnetic field detection, in Teslas 161 * @param model magnetic field model 162 * @param body Earth body shape 163 * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false) 164 * @param dataContext used to look up the magnetic field model. 165 */ 166 protected MagneticFieldDetector(final AdaptableInterval maxCheck, final double threshold, 167 final int maxIter, final EventHandler handler, 168 final double limit, final FieldModel model, final OneAxisEllipsoid body, 169 final boolean atSeaLevel, final DataContext dataContext) { 170 super(new EventDetectionSettings(maxCheck, threshold, maxIter), handler); 171 this.limit = limit; 172 this.model = model; 173 this.body = body; 174 this.atSeaLevel = atSeaLevel; 175 this.dataContext = dataContext; 176 } 177 178 /** {@inheritDoc} */ 179 @Override 180 protected MagneticFieldDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, 181 final int newMaxIter, final EventHandler newHandler) { 182 return new MagneticFieldDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, 183 limit, model, body, atSeaLevel, dataContext); 184 } 185 186 /** {@inheritDoc} */ 187 @Override 188 public void init(final SpacecraftState s0, final AbsoluteDate t) { 189 super.init(s0, t); 190 final TimeScale utc = dataContext.getTimeScales().getUTC(); 191 this.currentYear = s0.getDate().getComponents(utc).getDate().getYear(); 192 this.field = dataContext.getGeoMagneticFields().getField(model, currentYear); 193 } 194 195 /** Compute the value of the detection function. 196 * <p> 197 * The returned value is the difference between the field intensity at spacecraft location, 198 * taking <code>atSeaLevel</code> switch into account, and the fixed threshold value. 199 * </p> 200 * @param s the current state information: date, kinematics, attitude 201 * @return difference between the field intensity at spacecraft location 202 * and the fixed threshold value 203 */ 204 public double g(final SpacecraftState s) { 205 final TimeScale utc = dataContext.getTimeScales().getUTC(); 206 if (s.getDate().getComponents(utc).getDate().getYear() != currentYear) { 207 this.currentYear = s.getDate().getComponents(utc).getDate().getYear(); 208 this.field = dataContext.getGeoMagneticFields().getField(model, currentYear); 209 } 210 final GeodeticPoint geoPoint = body.transform(s.getPosition(), s.getFrame(), s.getDate()); 211 final double altitude = atSeaLevel ? 0. : geoPoint.getAltitude(); 212 final double value = field.calculateField(geoPoint.getLatitude(), geoPoint.getLongitude(), altitude).getTotalIntensity(); 213 return value - limit; 214 } 215 216 }