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 }