WalkerConstellation.java

  1. /* Copyright 2022-2025 Luc Maisonobe
  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.orbits;

  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.hipparchus.geometry.euclidean.threed.Rotation;
  21. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  22. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  23. import org.hipparchus.util.FastMath;
  24. import org.hipparchus.util.MathUtils;
  25. import org.orekit.errors.OrekitException;
  26. import org.orekit.errors.OrekitMessages;
  27. import org.orekit.utils.PVCoordinates;

  28. /** Builder for orbits of satellites forming a Walker constellation.
  29.  * <p>
  30.  * It  manages the 2 patterns:
  31.  * <ul>
  32.  * <li>Delta, with ascending nodes distributed over 360°</li>
  33.  * <li>Star, with ascending nodes distributed over 180°</li>
  34.  * </ul>
  35.  * @author Luc Maisonobe
  36.  * @since 12.1
  37.  */
  38. public class WalkerConstellation {

  39.     /** Total number of satellites. */
  40.     private final int t;

  41.     /** Number of orbital planes. */
  42.     private final int p;

  43.     /** Phasing parameter. */
  44.     private final int f;

  45.     /** Constellation pattern. */
  46.     private final Pattern pattern;

  47.     /** Default constructor for Walker Delta constellation.
  48.      * @param t total number of satellites
  49.      * @param p number of orbital planes
  50.      * @param f phasing parameter
  51.      */
  52.     public WalkerConstellation(final int t, final int p, final int f) {
  53.         this(t, p, f, Pattern.DELTA);
  54.     }

  55.     /** Complete constructor with the choice of the pattern.
  56.      * @param t       total number of satellites
  57.      * @param p       number of orbital planes
  58.      * @param f       phasing parameter
  59.      * @param pattern constellation pattern
  60.      */
  61.     public WalkerConstellation(final int t, final int p, final int f, final Pattern pattern) {
  62.         this.t       = t;
  63.         this.p       = p;
  64.         this.f       = f;
  65.         this.pattern = pattern;
  66.         if (t % p != 0) {
  67.             throw new OrekitException(OrekitMessages.WALKER_INCONSISTENT_PLANES, p, t);
  68.         }
  69.     }

  70.     /** Get the total number of satellites.
  71.      * @return total number of satellites
  72.      */
  73.     public int getT() {
  74.         return t;
  75.     }

  76.     /** Get the number of orbital planes.
  77.      * @return number of orbital planes
  78.      */
  79.     public int getP() {
  80.         return p;
  81.     }

  82.     /** Get the phasing parameter.
  83.      * @return phasing parameter
  84.      */
  85.     public int getF() {
  86.         return f;
  87.     }

  88.     /** Get the constellation pattern.
  89.      * @return constellation pattern
  90.      */
  91.     public Pattern getPattern() {
  92.         return pattern;
  93.     }

  94.     /** Create the regular slots.
  95.      * <p>
  96.      * This method builds the {@link #getT() T} regular satellite, with
  97.      * integer {@link WalkerConstellationSlot#getSatellite() satellite indices}. If
  98.      * additional in-orbit spare satellites must be created, the {@link
  99.      * #buildSlot(WalkerConstellationSlot, int, double) buildSlot} method must be called
  100.      * explicitly.
  101.      * </p>
  102.      * <p>
  103.      * The various orbits are built from the {@code referenceOrbit} using plane
  104.      * rotations and {@link Orbit#shiftedBy(double) shifts}. This implies that
  105.      * if orbit does not include non-Keplerian derivatives, a
  106.      * simple Keplerian motion is assumed, which is the intended use case.
  107.      * </p>
  108.      * @param <O> type of the orbits
  109.      * @param referenceOrbit orbit of the reference satellite, in
  110.      * {@link WalkerConstellationSlot#getPlane() plane} 0 and
  111.      * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0
  112.      * @return built orbits as a list of list, organized by planes
  113.      * @see #buildReferenceSlot(Orbit)
  114.      * @see #buildSlot(WalkerConstellationSlot, int, double)
  115.      */
  116.     public <O extends Orbit> List<List<WalkerConstellationSlot<O>>> buildRegularSlots(final O referenceOrbit) {

  117.         // build the reference slot
  118.         final WalkerConstellationSlot<O> referenceSlot = buildReferenceSlot(referenceOrbit);

  119.         final List<List<WalkerConstellationSlot<O>>> all = new ArrayList<>(p);
  120.         for (int plane = 0; plane < p; ++plane) {

  121.             // prepare list for one plane
  122.             final List<WalkerConstellationSlot<O>> planeSlots = new ArrayList<>(t / p);

  123.             // build all slots belonging to this plane
  124.             for (int satellite = 0; satellite < t / p; ++satellite) {
  125.                 planeSlots.add(plane == 0 && satellite == 0 ?
  126.                                referenceSlot :
  127.                                buildSlot(referenceSlot, plane, satellite));
  128.             }

  129.             // finished plane
  130.             all.add(planeSlots);

  131.         }

  132.         // return the complete constellation
  133.         return all;

  134.     }

  135.     /** Create the reference slot, which is satellite 0 in plane 0.
  136.      * @param <O> type of the orbits
  137.      * @param referenceOrbit orbit of the reference satellite, in
  138.      * {@link WalkerConstellationSlot#getPlane() plane} 0 and
  139.      * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0
  140.      * @return build reference slot
  141.      * @see #buildRegularSlots(Orbit)
  142.      * @see #buildSlot(WalkerConstellationSlot, int, double)
  143.      */
  144.     public <O extends Orbit> WalkerConstellationSlot<O>buildReferenceSlot(final O referenceOrbit) {
  145.         return new WalkerConstellationSlot<>(this, 0, 0, referenceOrbit);
  146.     }

  147.     /** Create one offset slot from an already existing slot.
  148.      * @param <O> type of the orbits
  149.      * @param existingSlot existing slot (may be the {@link #buildReferenceSlot(Orbit) reference slot} or not)
  150.      * @param plane plane index of the new slot (may be non-integer for in-orbit spare satellites)
  151.      * @param satellite new slot satellite index in plane (may be non-integer if needed)
  152.      * @return built slot
  153.      * @see #buildRegularSlots(Orbit)
  154.      * @see #buildReferenceSlot(Orbit)
  155.      */
  156.     public <O extends Orbit> WalkerConstellationSlot<O> buildSlot(final WalkerConstellationSlot<O> existingSlot,
  157.                                                                   final int plane, final double satellite) {

  158.         // offsets from existing slot
  159.         final O      refOrbit = existingSlot.getOrbit();
  160.         final int    dp       = plane - existingSlot.getPlane();
  161.         final double ds       = satellite - existingSlot.getSatellite();

  162.         // in plane shift
  163.         final double deltaT = (dp * f + ds * p) * refOrbit.getKeplerianPeriod() / t;
  164.         final Orbit shifted = refOrbit.shiftedBy(deltaT);

  165.         // plane rotation
  166.         final Rotation      r       = new Rotation(Vector3D.PLUS_K,
  167.                                                    pattern.getRaanDistribution() * dp / p,
  168.                                                    RotationConvention.VECTOR_OPERATOR);
  169.         final PVCoordinates pv      = shifted.getPVCoordinates();
  170.         final PVCoordinates rotated = new PVCoordinates(r.applyTo(pv.getPosition()),
  171.                                                         r.applyTo(pv.getVelocity()));

  172.         // build orbit
  173.         final CartesianOrbit c = new CartesianOrbit(rotated, refOrbit.getFrame(),
  174.                                                     refOrbit.getDate(), refOrbit.getMu());
  175.         @SuppressWarnings("unchecked")
  176.         final O orbit = (O) refOrbit.getType().convertType(c);

  177.         // build slot
  178.         return new WalkerConstellationSlot<>(this, plane, satellite, orbit);

  179.     }

  180.     /**
  181.      * Enumerate for Walker constellation design patterns.
  182.      */
  183.     public enum Pattern {

  184.         /** Delta pattern: ascending nodes distributed over 360°. */
  185.         DELTA {

  186.             /** {@inheritDoc} */
  187.             @Override
  188.             public double getRaanDistribution() {
  189.                 return MathUtils.TWO_PI;
  190.             }
  191.         },

  192.         /** Star pattern: ascending nodes distributed over 180°. */
  193.         STAR {

  194.             /** {@inheritDoc} */
  195.             @Override
  196.             public double getRaanDistribution() {
  197.                 return FastMath.PI;
  198.             }
  199.         };

  200.         /** Get the RAAN distribution for the pattern.
  201.          * @return the RAAN distribution for the pattern
  202.          */
  203.         public abstract double getRaanDistribution();
  204.     }
  205. }