MagneticFieldDetector.java

  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. import org.orekit.annotation.DefaultDataContext;
  19. import org.orekit.bodies.GeodeticPoint;
  20. import org.orekit.bodies.OneAxisEllipsoid;
  21. import org.orekit.data.DataContext;
  22. import org.orekit.models.earth.GeoMagneticField;
  23. import org.orekit.models.earth.GeoMagneticFieldFactory;
  24. import org.orekit.models.earth.GeoMagneticFieldFactory.FieldModel;
  25. import org.orekit.propagation.SpacecraftState;
  26. import org.orekit.propagation.events.handlers.EventHandler;
  27. import org.orekit.propagation.events.handlers.StopOnIncreasing;
  28. import org.orekit.time.AbsoluteDate;
  29. import org.orekit.time.TimeScale;

  30. /** Detector for Earth magnetic field strength.
  31.  * <p>
  32.  * The detector is based on the field intensity calculated at the
  33.  * satellite's latitude and longitude, either at sea level or at
  34.  * satellite altitude, depending on the value chosen for the
  35.  * <code>atSeaLevel</code> indicator.<br>
  36.  * It can detect flyovers of the South-Atlantic anomaly with
  37.  * a classically accepted limit value of 32,000 nT at sea level.
  38.  * </p>
  39.  * @author Romaric Her
  40.  */
  41. public class MagneticFieldDetector extends AbstractDetector<MagneticFieldDetector> {

  42.     /** Fixed threshold value of Magnetic field to be crossed, in Teslas. */
  43.     private final double limit;

  44.     /** Switch for calculating field strength at sea level (true) or satellite altitude (false). */
  45.     private final boolean atSeaLevel;

  46.     /** Earth geomagnetic field. */
  47.     private GeoMagneticField field;

  48.     /** year of the current state. */
  49.     private double currentYear;

  50.     /** Earth geomagnetic field model. */
  51.     private final FieldModel model;

  52.     /** Earth body shape. */
  53.     private final OneAxisEllipsoid body;

  54.     /** Current data context. */
  55.     private final DataContext dataContext;


  56.     /** Build a new detector.
  57.      *
  58.      * <p>This constructor uses:
  59.      * <ul>
  60.      * <li>the {@link DataContext#getDefault() default data context}</li>
  61.      * <li>the {@link AbstractDetector#DEFAULT_MAX_CHECK default value} for maximal checking interval</li>
  62.      * <li>the {@link AbstractDetector#DEFAULT_THRESHOLD default value} for convergence threshold</li>
  63.      * <li>the <code>atSeaLevel</code> switch set to false</li>
  64.      * </ul>
  65.      *
  66.      * @param limit threshold value for magnetic field detection, in Teslas
  67.      * @param model magnetic field model
  68.      * @param body  Earth body shape
  69.      * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext)
  70.      */
  71.     @DefaultDataContext
  72.     public MagneticFieldDetector(final double limit, final FieldModel model, final OneAxisEllipsoid body) {
  73.         this(DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, limit, model, body, false);
  74.     }

  75.     /** Build a new detector.
  76.      *
  77.      * <p>This constructor uses:
  78.      * <ul>
  79.      * <li>the {@link DataContext#getDefault() default data context}</li>
  80.      * <li>the {@link AbstractDetector#DEFAULT_MAX_CHECK default value} for maximal checking interval</li>
  81.      * <li>the {@link AbstractDetector#DEFAULT_THRESHOLD default value} for convergence threshold </li>
  82.      * </ul>
  83.      *
  84.      * @param limit    threshold value for magnetic field detection, in Teslas
  85.      * @param model    magnetic field model
  86.      * @param body     Earth body shape
  87.      * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false)
  88.      * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext)
  89.      */
  90.     @DefaultDataContext
  91.     public MagneticFieldDetector(final double limit, final FieldModel model,
  92.                                  final OneAxisEllipsoid body, final boolean atSeaLevel) {
  93.         this(DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, limit, model, body, atSeaLevel);
  94.     }

  95.     /** Build a detector.
  96.      *
  97.      * <p>This method uses the {@link DataContext#getDefault() default data context}.</p>
  98.      *
  99.      * @param maxCheck   maximal checking interval (s)
  100.      * @param threshold  convergence threshold (s)
  101.      * @param limit      threshold value for magnetic field detection, in Teslas
  102.      * @param model      magnetic field model
  103.      * @param body       Earth body shape
  104.      * @param atSeaLevel switch for calculating field intensity at sea level (true) or satellite altitude (false)
  105.      * @see #MagneticFieldDetector(double, double, double, GeoMagneticFieldFactory.FieldModel, OneAxisEllipsoid, boolean, DataContext)
  106.      */
  107.     @DefaultDataContext
  108.     public MagneticFieldDetector(final double maxCheck, final double threshold, final double limit,
  109.                                  final FieldModel model, final OneAxisEllipsoid body, final boolean atSeaLevel) {
  110.         this(maxCheck, threshold, limit, model, body, atSeaLevel, DataContext.getDefault());
  111.     }

  112.     /**
  113.      * Build a detector.
  114.      *
  115.      * @param maxCheck    maximal checking interval (s)
  116.      * @param threshold   convergence threshold (s)
  117.      * @param limit       threshold value for magnetic field detection, in Teslas
  118.      * @param model       magnetic field model
  119.      * @param body        Earth body shape
  120.      * @param atSeaLevel  switch for calculating field intensity at sea level (true) or satellite altitude (false)
  121.      * @param dataContext used to look up the magnetic field model.
  122.      * @since 10.1
  123.      */
  124.     public MagneticFieldDetector(final double maxCheck,
  125.                                  final double threshold,
  126.                                  final double limit,
  127.                                  final FieldModel model,
  128.                                  final OneAxisEllipsoid body,
  129.                                  final boolean atSeaLevel,
  130.                                  final DataContext dataContext) {
  131.         this(new EventDetectionSettings(maxCheck, threshold, DEFAULT_MAX_ITER), new StopOnIncreasing(),
  132.              limit, model, body, atSeaLevel, dataContext);
  133.     }

  134.     /** Protected constructor with full parameters.
  135.      * <p>
  136.      * This constructor is not public as users are expected to use the builder
  137.      * API with the various {@code withXxx()} methods to set up the instance
  138.      * in a readable manner without using a huge amount of parameters.
  139.      * </p>
  140.      * @param detectionSettings event detection settings
  141.      * @param handler     event handler to call at event occurrences
  142.      * @param limit       threshold value for magnetic field detection, in Teslas
  143.      * @param model       magnetic field model
  144.      * @param body        Earth body shape
  145.      * @param atSeaLevel  switch for calculating field intensity at sea level (true) or satellite altitude (false)
  146.      * @param dataContext used to look up the magnetic field model.
  147.      * @since 13.0
  148.      */
  149.     protected MagneticFieldDetector(final EventDetectionSettings detectionSettings, final EventHandler handler,
  150.                                     final double limit, final FieldModel model, final OneAxisEllipsoid body,
  151.                                     final boolean atSeaLevel, final DataContext dataContext) {
  152.         super(detectionSettings, handler);
  153.         this.limit       = limit;
  154.         this.model       = model;
  155.         this.body        = body;
  156.         this.atSeaLevel  = atSeaLevel;
  157.         this.dataContext = dataContext;
  158.     }

  159.     /** {@inheritDoc} */
  160.     @Override
  161.     protected MagneticFieldDetector create(final EventDetectionSettings detectionSettings, final EventHandler newHandler) {
  162.         return new MagneticFieldDetector(detectionSettings, newHandler, limit, model, body, atSeaLevel, dataContext);
  163.     }

  164.     /** {@inheritDoc} */
  165.     @Override
  166.     public void init(final SpacecraftState s0, final AbsoluteDate t) {
  167.         super.init(s0, t);
  168.         final TimeScale utc = dataContext.getTimeScales().getUTC();
  169.         this.currentYear = s0.getDate().getComponents(utc).getDate().getYear();
  170.         this.field = dataContext.getGeoMagneticFields().getField(model, currentYear);
  171.     }

  172.     /** Compute the value of the detection function.
  173.      * <p>
  174.      * The returned value is the difference between the field intensity at spacecraft location,
  175.      * taking <code>atSeaLevel</code> switch into account, and the fixed threshold value.
  176.      * </p>
  177.      * @param s the current state information: date, kinematics, attitude
  178.      * @return difference between the field intensity at spacecraft location
  179.      *         and the fixed threshold value
  180.      */
  181.     public double g(final SpacecraftState s) {
  182.         final TimeScale utc = dataContext.getTimeScales().getUTC();
  183.         if (s.getDate().getComponents(utc).getDate().getYear() != currentYear) {
  184.             this.currentYear = s.getDate().getComponents(utc).getDate().getYear();
  185.             this.field = dataContext.getGeoMagneticFields().getField(model, currentYear);
  186.         }
  187.         final GeodeticPoint geoPoint = body.transform(s.getPosition(), s.getFrame(), s.getDate());
  188.         final double altitude = atSeaLevel ? 0. : geoPoint.getAltitude();
  189.         final double value = field.calculateField(geoPoint.getLatitude(), geoPoint.getLongitude(), altitude).getTotalIntensity();
  190.         return value - limit;
  191.     }

  192. }