1   /* Copyright 2002-2024 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  
19  import org.hipparchus.geometry.euclidean.threed.Rotation;
20  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.MathUtils;
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitMessages;
25  import org.orekit.utils.PVCoordinates;
26  
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  /** Builder for orbits of satellites forming a Walker constellation.
31   * @author Luc Maisonobe
32   * @since 12.1
33   */
34  public class WalkerConstellation {
35  
36      /** Total number of satellites. */
37      private final int t;
38  
39      /** Number of orbital planes. */
40      private final int p;
41  
42      /** Phasing parameter. */
43      private final int f;
44  
45      /** Simple constructor.
46       * @param t total number of satellites
47       * @param p number of orbital planes
48       * @param f phasing parameter
49       */
50      public WalkerConstellation(final int t, final int p, final int f) {
51          this.t = t;
52          this.p = p;
53          this.f = f;
54          if (t % p != 0) {
55              throw new OrekitException(OrekitMessages.WALKER_INCONSISTENT_PLANES, p, t);
56          }
57      }
58  
59      /** Get the total number of satellites.
60       * @return total number of satellites
61       */
62      public int getT() {
63          return t;
64      }
65  
66      /** Get the number of orbital planes.
67       * @return number of orbital planes
68       */
69      public int getP() {
70          return p;
71      }
72  
73      /** Get the phasing parameter.
74       * @return phasing parameter
75       */
76      public int getF() {
77          return f;
78      }
79  
80      /** Create the regular slots.
81       * <p>
82       * This method builds the {@link #getT() T} regular satellite, with
83       * integer {@link WalkerConstellationSlot#getSatellite() satellite indices}. If
84       * additional in-orbit spare satellites must be created, the {@link
85       * #buildSlot(WalkerConstellationSlot, int, double) buildSlot} method must be called
86       * explicitly.
87       * </p>
88       * <p>
89       * The various orbits are built from the {@code referenceOrbit} using plane
90       * rotations and {@link Orbit#shiftedBy(double) shifts}. This implies that
91       * if orbit does not include {@link Orbit#hasDerivatives() derivatives}, a
92       * simple Keplerian motion is assumed, which is the intended use case.
93       * </p>
94       * @param <O> type of the orbits
95       * @param referenceOrbit orbit of the reference satellite, in
96       * {@link WalkerConstellationSlot#getPlane() plane} 0 and
97       * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0
98       * @return built orbits as a list of list, organized by planes
99       * @see #buildReferenceSlot(Orbit)
100      * @see #buildSlot(WalkerConstellationSlot, int, double)
101      */
102     public <O extends Orbit> List<List<WalkerConstellationSlot<O>>> buildRegularSlots(final O referenceOrbit) {
103 
104         // build the reference slot
105         final WalkerConstellationSlot<O> referenceSlot = buildReferenceSlot(referenceOrbit);
106 
107         final List<List<WalkerConstellationSlot<O>>> all = new ArrayList<>(p);
108         for (int plane = 0; plane < p; ++plane) {
109 
110             // prepare list for one plane
111             final List<WalkerConstellationSlot<O>> planeSlots = new ArrayList<>(t / p);
112 
113             // build all slots belonging to this plane
114             for (int satellite = 0; satellite < t / p; ++satellite) {
115                 planeSlots.add(plane == 0 && satellite == 0 ?
116                                referenceSlot :
117                                buildSlot(referenceSlot, plane, satellite));
118             }
119 
120             // finished plane
121             all.add(planeSlots);
122 
123         }
124 
125         // return the complete constellation
126         return all;
127 
128     }
129 
130     /** Create the reference slot, which is satellite 0 in plane 0.
131      * @param <O> type of the orbits
132      * @param referenceOrbit orbit of the reference satellite, in
133      * {@link WalkerConstellationSlot#getPlane() plane} 0 and
134      * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0
135      * @return build reference slot
136      * @see #buildRegularSlots(Orbit)
137      * @see #buildSlot(WalkerConstellationSlot, int, double)
138      */
139     public <O extends Orbit> WalkerConstellationSlot<O>buildReferenceSlot(final O referenceOrbit) {
140         return new WalkerConstellationSlot<>(this, 0, 0, referenceOrbit);
141     }
142 
143     /** Create one offset slot from an already existing slot.
144      * @param <O> type of the orbits
145      * @param existingSlot existing slot (may be the {@link #buildReferenceSlot(Orbit) reference slot} or not)
146      * @param plane plane index of the new slot (may be non-integer for in-orbit spare satellites)
147      * @param satellite new slot satellite index in plane (may be non-integer if needed)
148      * @return built slot
149      * @see #buildRegularSlots(Orbit)
150      * @see #buildReferenceSlot(Orbit)
151      */
152     public <O extends Orbit> WalkerConstellationSlot<O> buildSlot(final WalkerConstellationSlot<O> existingSlot,
153                                                                   final int plane, final double satellite) {
154 
155         // offsets from existing slot
156         final O      refOrbit = existingSlot.getOrbit();
157         final int    dp       = plane - existingSlot.getPlane();
158         final double ds       = satellite - existingSlot.getSatellite();
159 
160         // in plane shift
161         final double deltaT = (dp * f + ds * p) * refOrbit.getKeplerianPeriod() / t;
162         final Orbit shifted = refOrbit.shiftedBy(deltaT);
163 
164         // plane rotation
165         final Rotation      r       = new Rotation(Vector3D.PLUS_K,
166                                                    MathUtils.TWO_PI * dp / p,
167                                                    RotationConvention.VECTOR_OPERATOR);
168         final PVCoordinates pv      = shifted.getPVCoordinates();
169         final PVCoordinates rotated = new PVCoordinates(r.applyTo(pv.getPosition()),
170                                                         r.applyTo(pv.getVelocity()));
171 
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 
178         // build slot
179         return new WalkerConstellationSlot<>(this, plane, satellite, orbit);
180 
181     }
182 
183 }