WalkerConstellation.java
- /* Copyright 2022-2025 Luc Maisonobe
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.orbits;
- import java.util.ArrayList;
- import java.util.List;
- import org.hipparchus.geometry.euclidean.threed.Rotation;
- import org.hipparchus.geometry.euclidean.threed.RotationConvention;
- import org.hipparchus.geometry.euclidean.threed.Vector3D;
- import org.hipparchus.util.FastMath;
- import org.hipparchus.util.MathUtils;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.utils.PVCoordinates;
- /** Builder for orbits of satellites forming a Walker constellation.
- * <p>
- * It manages the 2 patterns:
- * <ul>
- * <li>Delta, with ascending nodes distributed over 360°</li>
- * <li>Star, with ascending nodes distributed over 180°</li>
- * </ul>
- * @author Luc Maisonobe
- * @since 12.1
- */
- public class WalkerConstellation {
- /** Total number of satellites. */
- private final int t;
- /** Number of orbital planes. */
- private final int p;
- /** Phasing parameter. */
- private final int f;
- /** Constellation pattern. */
- private final Pattern pattern;
- /** Default constructor for Walker Delta constellation.
- * @param t total number of satellites
- * @param p number of orbital planes
- * @param f phasing parameter
- */
- public WalkerConstellation(final int t, final int p, final int f) {
- this(t, p, f, Pattern.DELTA);
- }
- /** Complete constructor with the choice of the pattern.
- * @param t total number of satellites
- * @param p number of orbital planes
- * @param f phasing parameter
- * @param pattern constellation pattern
- */
- public WalkerConstellation(final int t, final int p, final int f, final Pattern pattern) {
- this.t = t;
- this.p = p;
- this.f = f;
- this.pattern = pattern;
- if (t % p != 0) {
- throw new OrekitException(OrekitMessages.WALKER_INCONSISTENT_PLANES, p, t);
- }
- }
- /** Get the total number of satellites.
- * @return total number of satellites
- */
- public int getT() {
- return t;
- }
- /** Get the number of orbital planes.
- * @return number of orbital planes
- */
- public int getP() {
- return p;
- }
- /** Get the phasing parameter.
- * @return phasing parameter
- */
- public int getF() {
- return f;
- }
- /** Get the constellation pattern.
- * @return constellation pattern
- */
- public Pattern getPattern() {
- return pattern;
- }
- /** Create the regular slots.
- * <p>
- * This method builds the {@link #getT() T} regular satellite, with
- * integer {@link WalkerConstellationSlot#getSatellite() satellite indices}. If
- * additional in-orbit spare satellites must be created, the {@link
- * #buildSlot(WalkerConstellationSlot, int, double) buildSlot} method must be called
- * explicitly.
- * </p>
- * <p>
- * The various orbits are built from the {@code referenceOrbit} using plane
- * rotations and {@link Orbit#shiftedBy(double) shifts}. This implies that
- * if orbit does not include non-Keplerian derivatives, a
- * simple Keplerian motion is assumed, which is the intended use case.
- * </p>
- * @param <O> type of the orbits
- * @param referenceOrbit orbit of the reference satellite, in
- * {@link WalkerConstellationSlot#getPlane() plane} 0 and
- * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0
- * @return built orbits as a list of list, organized by planes
- * @see #buildReferenceSlot(Orbit)
- * @see #buildSlot(WalkerConstellationSlot, int, double)
- */
- public <O extends Orbit> List<List<WalkerConstellationSlot<O>>> buildRegularSlots(final O referenceOrbit) {
- // build the reference slot
- final WalkerConstellationSlot<O> referenceSlot = buildReferenceSlot(referenceOrbit);
- final List<List<WalkerConstellationSlot<O>>> all = new ArrayList<>(p);
- for (int plane = 0; plane < p; ++plane) {
- // prepare list for one plane
- final List<WalkerConstellationSlot<O>> planeSlots = new ArrayList<>(t / p);
- // build all slots belonging to this plane
- for (int satellite = 0; satellite < t / p; ++satellite) {
- planeSlots.add(plane == 0 && satellite == 0 ?
- referenceSlot :
- buildSlot(referenceSlot, plane, satellite));
- }
- // finished plane
- all.add(planeSlots);
- }
- // return the complete constellation
- return all;
- }
- /** Create the reference slot, which is satellite 0 in plane 0.
- * @param <O> type of the orbits
- * @param referenceOrbit orbit of the reference satellite, in
- * {@link WalkerConstellationSlot#getPlane() plane} 0 and
- * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0
- * @return build reference slot
- * @see #buildRegularSlots(Orbit)
- * @see #buildSlot(WalkerConstellationSlot, int, double)
- */
- public <O extends Orbit> WalkerConstellationSlot<O>buildReferenceSlot(final O referenceOrbit) {
- return new WalkerConstellationSlot<>(this, 0, 0, referenceOrbit);
- }
- /** Create one offset slot from an already existing slot.
- * @param <O> type of the orbits
- * @param existingSlot existing slot (may be the {@link #buildReferenceSlot(Orbit) reference slot} or not)
- * @param plane plane index of the new slot (may be non-integer for in-orbit spare satellites)
- * @param satellite new slot satellite index in plane (may be non-integer if needed)
- * @return built slot
- * @see #buildRegularSlots(Orbit)
- * @see #buildReferenceSlot(Orbit)
- */
- public <O extends Orbit> WalkerConstellationSlot<O> buildSlot(final WalkerConstellationSlot<O> existingSlot,
- final int plane, final double satellite) {
- // offsets from existing slot
- final O refOrbit = existingSlot.getOrbit();
- final int dp = plane - existingSlot.getPlane();
- final double ds = satellite - existingSlot.getSatellite();
- // in plane shift
- final double deltaT = (dp * f + ds * p) * refOrbit.getKeplerianPeriod() / t;
- final Orbit shifted = refOrbit.shiftedBy(deltaT);
- // plane rotation
- final Rotation r = new Rotation(Vector3D.PLUS_K,
- pattern.getRaanDistribution() * dp / p,
- RotationConvention.VECTOR_OPERATOR);
- final PVCoordinates pv = shifted.getPVCoordinates();
- final PVCoordinates rotated = new PVCoordinates(r.applyTo(pv.getPosition()),
- r.applyTo(pv.getVelocity()));
- // build orbit
- final CartesianOrbit c = new CartesianOrbit(rotated, refOrbit.getFrame(),
- refOrbit.getDate(), refOrbit.getMu());
- @SuppressWarnings("unchecked")
- final O orbit = (O) refOrbit.getType().convertType(c);
- // build slot
- return new WalkerConstellationSlot<>(this, plane, satellite, orbit);
- }
- /**
- * Enumerate for Walker constellation design patterns.
- */
- public enum Pattern {
- /** Delta pattern: ascending nodes distributed over 360°. */
- DELTA {
- /** {@inheritDoc} */
- @Override
- public double getRaanDistribution() {
- return MathUtils.TWO_PI;
- }
- },
- /** Star pattern: ascending nodes distributed over 180°. */
- STAR {
- /** {@inheritDoc} */
- @Override
- public double getRaanDistribution() {
- return FastMath.PI;
- }
- };
- /** Get the RAAN distribution for the pattern.
- * @return the RAAN distribution for the pattern
- */
- public abstract double getRaanDistribution();
- }
- }