FieldLongitudeCrossingDetector.java

  1. /* Copyright 2023-2025 Alberto Ferrero
  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.  * Alberto Ferrero 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.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.util.FastMath;
  21. import org.hipparchus.util.MathUtils;
  22. import org.orekit.bodies.FieldGeodeticPoint;
  23. import org.orekit.bodies.OneAxisEllipsoid;
  24. import org.orekit.propagation.FieldSpacecraftState;
  25. import org.orekit.propagation.events.handlers.FieldContinueOnEvent;
  26. import org.orekit.propagation.events.handlers.FieldEventHandler;
  27. import org.orekit.propagation.events.handlers.FieldStopOnIncreasing;
  28. import org.orekit.propagation.events.intervals.FieldAdaptableInterval;
  29. import org.orekit.time.FieldAbsoluteDate;

  30. /** Detector for geographic longitude crossing.
  31.  * <p>This detector identifies when a spacecraft crosses a fixed
  32.  * longitude with respect to a central body.</p>
  33.  * @author Alberto Ferrero
  34.  * @since 12.0
  35.  * @param <T> type of the field elements
  36.  */
  37. public class FieldLongitudeCrossingDetector <T extends CalculusFieldElement<T>>
  38.     extends FieldAbstractDetector<FieldLongitudeCrossingDetector<T>, T> {

  39.     /**
  40.     * Body on which the longitude is defined.
  41.     */
  42.     private final OneAxisEllipsoid body;

  43.     /**
  44.     * Fixed longitude to be crossed.
  45.     */
  46.     private final double longitude;

  47.     /**
  48.     * Filtering detector.
  49.     */
  50.     private final FieldEventEnablingPredicateFilter<T> filtering;

  51.     /**
  52.     * Build a new detector.
  53.     * <p>The new instance uses default values for maximal checking interval
  54.     * ({@link #DEFAULT_MAX_CHECK}) and convergence threshold ({@link
  55.     * #DEFAULT_THRESHOLD}).</p>
  56.     *
  57.     * @param field     the type of numbers to use.
  58.     * @param body      body on which the longitude is defined
  59.     * @param longitude longitude to be crossed
  60.     */
  61.     public FieldLongitudeCrossingDetector(final Field<T> field, final OneAxisEllipsoid body, final double longitude) {
  62.         this(new FieldEventDetectionSettings<>(field, EventDetectionSettings.getDefaultEventDetectionSettings()),
  63.                 new FieldStopOnIncreasing<>(), body, longitude);
  64.     }

  65.     /**
  66.     * Build a detector.
  67.     *
  68.     * @param maxCheck  maximal checking interval (s)
  69.     * @param threshold convergence threshold (s)
  70.     * @param body      body on which the longitude is defined
  71.     * @param longitude longitude to be crossed
  72.     */
  73.     public FieldLongitudeCrossingDetector(final T maxCheck,
  74.                                           final T threshold,
  75.                                           final OneAxisEllipsoid body,
  76.                                           final double longitude) {
  77.         this(new FieldEventDetectionSettings<>(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER),
  78.                 new FieldStopOnIncreasing<>(), body, longitude);
  79.     }

  80.     /**
  81.     * Protected constructor with full parameters.
  82.     * <p>
  83.     * This constructor is not public as users are expected to use the builder
  84.     * API with the various {@code withXxx()} methods to set up the instance
  85.     * in a readable manner without using a huge amount of parameters.
  86.     * </p>
  87.     *
  88.     * @param detectionSettings event detection settings
  89.     * @param handler   event handler to call at event occurrences
  90.     * @param body      body on which the longitude is defined
  91.     * @param longitude longitude to be crossed
  92.     */
  93.     protected FieldLongitudeCrossingDetector(
  94.         final FieldEventDetectionSettings<T> detectionSettings,
  95.         final FieldEventHandler<T> handler,
  96.         final OneAxisEllipsoid body,
  97.         final double longitude) {

  98.         super(detectionSettings, handler);

  99.         this.body = body;
  100.         this.longitude = longitude;

  101.         // we filter out spurious longitude crossings occurring at the antimeridian
  102.         final FieldRawLongitudeCrossingDetector<T> raw = new FieldRawLongitudeCrossingDetector<>(detectionSettings,
  103.             new FieldContinueOnEvent<>());
  104.         final FieldEnablingPredicate<T> predicate =
  105.             (state, detector, g) -> FastMath.abs(g).getReal() < 0.5 * FastMath.PI;
  106.         this.filtering = new FieldEventEnablingPredicateFilter<>(raw, predicate);

  107.     }

  108.     /**
  109.     * {@inheritDoc}
  110.     */
  111.     @Override
  112.     protected FieldLongitudeCrossingDetector<T> create(
  113.         final FieldEventDetectionSettings<T> detectionSettings, final FieldEventHandler<T> newHandler) {
  114.         return new FieldLongitudeCrossingDetector<>(detectionSettings, newHandler, body, longitude);
  115.     }

  116.     /**
  117.     * Get the body on which the geographic zone is defined.
  118.     *
  119.     * @return body on which the geographic zone is defined
  120.     */
  121.     public OneAxisEllipsoid getBody() {
  122.         return body;
  123.     }

  124.     /**
  125.     * Get the fixed longitude to be crossed (radians).
  126.     *
  127.     * @return fixed longitude to be crossed (radians)
  128.     */
  129.     public double getLongitude() {
  130.         return longitude;
  131.     }

  132.     /**
  133.     * {@inheritDoc}
  134.     */
  135.     @Override
  136.     public void init(final FieldSpacecraftState<T> s0, final FieldAbsoluteDate<T> t) {
  137.         super.init(s0, t);
  138.         filtering.init(s0, t);
  139.     }

  140.     /**
  141.     * Compute the value of the detection function.
  142.     * <p>
  143.     * The value is the longitude difference between the spacecraft and the fixed
  144.     * longitude to be crossed, with some sign tweaks to ensure continuity.
  145.     * These tweaks imply the {@code increasing} flag in events detection becomes
  146.     * irrelevant here! As an example, the longitude of a prograde spacecraft
  147.     * will always increase, but this g function will increase and decrease so it
  148.     * will cross the zero value once per orbit, in increasing and decreasing
  149.     * directions on alternate orbits. If eastwards and westwards crossing have to
  150.     * be distinguished, the velocity direction has to be checked instead of looking
  151.     * at the {@code increasing} flag.
  152.     * </p>
  153.     *
  154.     * @param s the current state information: date, kinematics, attitude
  155.     * @return longitude difference between the spacecraft and the fixed
  156.     * longitude, with some sign tweaks to ensure continuity
  157.     */
  158.     public T g(final FieldSpacecraftState<T> s) {
  159.         return filtering.g(s);
  160.     }

  161.     private class FieldRawLongitudeCrossingDetector <TT extends CalculusFieldElement<TT>>
  162.         extends FieldAbstractDetector<FieldRawLongitudeCrossingDetector<TT>, TT> {

  163.         /**
  164.         * Protected constructor with full parameters.
  165.         * <p>
  166.         * This constructor is not public as users are expected to use the builder
  167.         * API with the various {@code withXxx()} methods to set up the instance
  168.         * in a readable manner without using a huge amount of parameters.
  169.         * </p>
  170.         *
  171.         * @param detectionSettings event detection settings
  172.         * @param handler   event handler to call at event occurrences
  173.         */
  174.         protected FieldRawLongitudeCrossingDetector(
  175.             final FieldEventDetectionSettings<TT> detectionSettings,
  176.             final FieldEventHandler<TT> handler) {
  177.             super(detectionSettings, handler);
  178.         }

  179.         /**
  180.         * {@inheritDoc}
  181.         */
  182.         @Override
  183.         protected FieldRawLongitudeCrossingDetector<TT> create(
  184.             final FieldEventDetectionSettings<TT> detectionSettings,
  185.             final FieldEventHandler<TT> newHandler) {
  186.             return new FieldRawLongitudeCrossingDetector<>(detectionSettings, newHandler);
  187.         }

  188.         /**
  189.         * Compute the value of the detection function.
  190.         * <p>
  191.         * The value is the longitude difference between the spacecraft and the fixed
  192.         * longitude to be crossed, and it <em>does</em> change sign twice around
  193.         * the central body: once at expected longitude and once at antimeridian.
  194.         * The second sign change is a spurious one and is filtered out by the
  195.         * outer class.
  196.         * </p>
  197.         *
  198.         * @param s the current state information: date, kinematics, attitude
  199.         * @return longitude difference between the spacecraft and the fixed
  200.         * longitude
  201.         */
  202.         public TT g(final FieldSpacecraftState<TT> s) {

  203.             // convert state to geodetic coordinates
  204.             final FieldGeodeticPoint<TT> gp = body.transform(s.getPosition(),
  205.                 s.getFrame(), s.getDate());

  206.             // longitude difference
  207.             final TT zero = gp.getLongitude().getField().getZero();
  208.             return MathUtils.normalizeAngle(gp.getLongitude().subtract(longitude), zero);

  209.         }

  210.     }

  211. }