1 /* Copyright 2002-2025 CS GROUP
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.frames;
18
19 import org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.Field;
21 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
22 import org.hipparchus.geometry.euclidean.threed.Rotation;
23 import org.hipparchus.geometry.euclidean.threed.Vector3D;
24 import org.orekit.bodies.OneAxisEllipsoid;
25 import org.orekit.models.earth.GeoMagneticField;
26 import org.orekit.models.earth.ReferenceEllipsoid;
27 import org.orekit.time.AbsoluteDate;
28 import org.orekit.time.FieldAbsoluteDate;
29 import org.orekit.utils.FieldPVCoordinates;
30 import org.orekit.utils.PVCoordinates;
31
32 /**
33 * This class handles a magnetic field variation attitude provider.
34 * <p>
35 * It was designed to be used as a Bdot attitude pointing law which align a specific body axis with Earth magnetic field
36 * vector.
37 * <p>
38 * Attitude control thought the magnetic field is called Bdot as it follows the sinusoidal variation of the Earth magnetic
39 * field vector, along the orbit. Magnetorquers are used on board to align the instrument, as so the satellite, with the
40 * planet magnetic field, producing a sinusoidal torque along the orbit.
41 *
42 * @author Alberto Ferrero
43 * @author Vincent Cucchietti
44 */
45 public class LocalMagneticFieldFrame implements LOF {
46
47 /** Inertial frame in which position-velocity coordinates will be given when computing transform and rotation. */
48 private final Frame inertialFrame;
49
50 /** Vector used to define the local orbital frame. */
51 private final LOFBuilderVector lofBuilderVector;
52
53 /** Body shape. */
54 private final OneAxisEllipsoid wgs84BodyShape;
55
56 /** Earth's magnetic field. */
57 private final GeoMagneticField magneticField;
58
59 /**
60 * Constructor with default definition of the local orbital frame:
61 * <ul>
62 * <li> x: Magnetic field</li>
63 * <li> y: Completes orthonormal frame</li>
64 * <li> z: Cross product of the magnetic field with the orbital momentum</li>
65 * </ul>.
66 * <b>BEWARE : Do not use this constructor if it is planned to be used with an equatorial orbit as the magnetic field and
67 * orbital momentum vectors will be parallel and cause an error to be thrown</b>
68 *
69 * @param inertialFrame inertial frame in which position-velocity coordinates will be given when computing transform and
70 * rotation
71 * @param magneticField Earth magnetic field model
72 * @param bodyFrame body frame related to body shape
73 */
74 public LocalMagneticFieldFrame(final Frame inertialFrame,
75 final GeoMagneticField magneticField,
76 final Frame bodyFrame) {
77 this(inertialFrame, magneticField, LOFBuilderVector.PLUS_MOMENTUM, bodyFrame);
78 }
79
80 /**
81 * Constructor with custom definition of the local orbital frame:
82 * <ul>
83 * <li> x: Magnetic field</li>
84 * <li> y: Completes orthonormal frame</li>
85 * <li> z: Cross product of the magnetic field with chosen {@link LOFBuilderVector vector}</li>
86 * </ul>
87 * For near-polar orbits, it is suggested to use the {@link LOFBuilderVector orbital momentum} to define the local
88 * orbital frame. However, for near-equatorial orbits, it is advised to use either the
89 * {@link LOFBuilderVector position or the velocity}.
90 *
91 * @param inertialFrame inertial frame in which position-velocity coordinates will be given when computing transform and
92 * rotation
93 * @param magneticField Earth magnetic field model
94 * @param lofBuilderVector vector used to define the local orbital frame
95 * @param bodyFrame body frame related to body shape
96 */
97 public LocalMagneticFieldFrame(final Frame inertialFrame, final GeoMagneticField magneticField,
98 final LOFBuilderVector lofBuilderVector,
99 final Frame bodyFrame) {
100 this.inertialFrame = inertialFrame;
101 this.magneticField = magneticField;
102 this.lofBuilderVector = lofBuilderVector;
103 // Default WGS84 body shape as this is the one used by default in GeoMagneticField
104 this.wgs84BodyShape = ReferenceEllipsoid.getWgs84(bodyFrame);
105 }
106
107 /**
108 * {@inheritDoc} Direction as X axis aligned with magnetic field vector, Y axis aligned with the cross product of the
109 * magnetic field vector with chosen {@link LOFBuilderVector vector type}.
110 * <p>
111 * <b>BEWARE: In this implementation, the method simply fieldify the normal rotation with given field.
112 * Hence all derivatives are lost.</b>
113 */
114 @Override
115 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
116 final FieldAbsoluteDate<T> date,
117 final FieldPVCoordinates<T> pv) {
118 // TODO Implement field equivalent of "calculateField" method in GeoMagneticField
119 final Rotation rotation = rotationFromInertial(date.toAbsoluteDate(), pv.toPVCoordinates());
120 return new FieldRotation<>(field, rotation);
121 }
122
123 /**
124 * {@inheritDoc} Direction as X axis aligned with magnetic field vector, Z axis aligned with the cross product of the
125 * magnetic field vector with chosen {@link LOFBuilderVector vector type}.
126 */
127 @Override
128 public Rotation rotationFromInertial(final AbsoluteDate date, final PVCoordinates pv) {
129 // Express satellite coordinates in body frame
130 final StaticTransform inertialToBodyFrame = inertialFrame.getStaticTransformTo(wgs84BodyShape.getBodyFrame(), date);
131 final Vector3D posBody = inertialToBodyFrame.transformPosition(pv.getPosition());
132
133 // Compute satellite coordinates LLA and magnetic field vector in body frame
134 final double lat = posBody.getDelta();
135 final double lng = posBody.getAlpha();
136 final double alt = posBody.getNorm() - wgs84BodyShape.getEquatorialRadius();
137 final Vector3D magnVectorBody = magneticField.calculateField(lat, lng, alt).getFieldVector();
138
139 // Compute magnetic field in inertial frame
140 final StaticTransform bodyToInertialFrame = inertialToBodyFrame.getInverse();
141 final Vector3D magnVector = bodyToInertialFrame.transformVector(magnVectorBody);
142
143 return new Rotation(magnVector, magnVector.crossProduct(lofBuilderVector.getVector(pv)),
144 Vector3D.PLUS_I, Vector3D.PLUS_K);
145 }
146
147 /** {@inheritDoc} */
148 @Override
149 public String getName() {
150 return "LOCAL_MAGNETIC_FIELD_FRAME";
151 }
152
153 /** Get interlai frame.
154 * @return inertial frame
155 */
156 public Frame getInertialFrame() {
157 return inertialFrame;
158 }
159
160 /** Get geomagnetid field.
161 * @return geo magnetic field
162 */
163 public GeoMagneticField getMagneticField() {
164 return magneticField;
165 }
166
167 /**
168 * Enum defining how the +j axis of the local orbital frame will be defined.
169 * <p>
170 * For example, if {@code MINUS_MOMENTUM} is chosen, +j aligned as the cross product of the momentum vector and the
171 * magnetic field vector. The resulting body frame will be +x aligned with magnetic field, +z aligned with negative
172 * momentum, +y orthonormal.
173 */
174 public enum LOFBuilderVector {
175
176 /** Positive position vector. */
177 PLUS_POSITION {
178 /** {@inheritDoc} */
179 @Override
180 Vector3D getVector(final PVCoordinates pv) {
181 return pv.getPosition();
182 }
183 },
184
185 /** Positive velocity vector. */
186 PLUS_VELOCITY {
187 /** {@inheritDoc} */
188 @Override
189 Vector3D getVector(final PVCoordinates pv) {
190 return pv.getVelocity();
191 }
192 },
193
194 /** Positive orbital momentum vector. */
195 PLUS_MOMENTUM {
196 /** {@inheritDoc} */
197 @Override
198 Vector3D getVector(final PVCoordinates pv) {
199 return pv.getMomentum();
200 }
201 },
202
203 /** Negative position vector. */
204 MINUS_POSITION {
205 /** {@inheritDoc} */
206 @Override
207 Vector3D getVector(final PVCoordinates pv) {
208 return pv.getPosition().negate();
209 }
210 },
211
212 /** Negative velocity vector. */
213 MINUS_VELOCITY {
214 /** {@inheritDoc} */
215 @Override
216 Vector3D getVector(final PVCoordinates pv) {
217 return pv.getVelocity().negate();
218 }
219 },
220
221 /** Negative orbital momentum vector. */
222 MINUS_MOMENTUM {
223 /** {@inheritDoc} */
224 @Override
225 Vector3D getVector(final PVCoordinates pv) {
226 return pv.getMomentum().negate();
227 }
228 };
229
230 /**
231 * @param pv position-velocity coordinates expressed in the instance inertial frame
232 *
233 * @return Vector used to define the local orbital frame by computing the cross product of the magnetic field with
234 * this
235 */
236 abstract Vector3D getVector(PVCoordinates pv);
237 }
238
239 }