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 java.util.concurrent.atomic.AtomicReference;
20
21 import org.hipparchus.CalculusFieldElement;
22 import org.orekit.errors.FrameAncestorException;
23 import org.orekit.errors.OrekitMessages;
24 import org.orekit.time.AbsoluteDate;
25 import org.orekit.time.FieldAbsoluteDate;
26
27
28 /** Frame whose transform from its parent can be updated.
29 * <p>This class allows to control the relative position of two parts
30 * of the global frames tree using any two frames in each part as
31 * control handles. Consider the following simplified frames tree as an
32 * example:</p>
33 * <pre>
34 * GCRF
35 * |
36 * --------------------------------
37 * | | |
38 * Sun satellite Earth
39 * | |
40 * on-board antenna ground station
41 * |
42 * tracking antenna
43 * </pre>
44 * <p>Tracking measurements really correspond to the link between the ground
45 * and on-board antennas. This is tightly linked to the transform between
46 * these two frames, however neither frame is the direct parent frame of the
47 * other one: the path involves four intermediate frames. When we process a
48 * measurement, what we really want to update is the transform that defines
49 * the satellite frame with respect to its parent GCRF frame.</p>
50 * <p>In order to implement the above case, the satellite frame is defined
51 * as an instance of this class and its {@link #updateTransform(Frame, Frame,
52 * Transform, AbsoluteDate) updateTransform} would be called each time we want
53 * to adjust the frame, i.e. each time we get a new measurement between the
54 * two antennas.</p>
55 * @author Luc Maisonobe
56 */
57 public class UpdatableFrame extends Frame {
58
59 /** Build a non-inertial frame from its transform with respect to its parent.
60 * <p>calling this constructor is equivalent to call
61 * {@link #UpdatableFrame(Frame, Transform, String, boolean)
62 * UpdatableFrame(parent, transform, name, false)}.</p>
63 * @param parent parent frame (must be non-null)
64 * @param transform transform from parent frame to instance
65 * @param name name of the frame
66 * @exception IllegalArgumentException if the parent frame is null
67 */
68 public UpdatableFrame(final Frame parent, final Transform transform, final String name)
69 throws IllegalArgumentException {
70 this(parent, transform, name, false);
71 }
72
73 /** Build a frame from its transform with respect to its parent.
74 * <p>The convention for the transform is that it is from parent
75 * frame to instance. This means that the two following frames
76 * are similar:</p>
77 * <pre>
78 * Frame frame1 = new Frame(FramesFactory.getGCRF(), new Transform(t1, t2));
79 * Frame frame2 = new Frame(new Frame(FramesFactory.getGCRF(), t1), t2);
80 * </pre>
81 * @param parent parent frame (must be non-null)
82 * @param transform transform from parent frame to instance
83 * @param name name of the frame
84 * @param pseudoInertial true if frame is considered pseudo-inertial
85 * (i.e. suitable for propagating orbit)
86 * @exception IllegalArgumentException if the parent frame is null
87 */
88 public UpdatableFrame(final Frame parent, final Transform transform, final String name,
89 final boolean pseudoInertial)
90 throws IllegalArgumentException {
91 super(parent, new UpdatableProvider(transform), name, pseudoInertial);
92 }
93
94 /** Update the transform from parent frame implicitly according to two other
95 * frames.
96
97 * <p>This method allows to control the relative position of two parts
98 * of the global frames tree using any two frames in each part as
99 * control handles. Consider the following simplified frames tree as an
100 * example:</p>
101 * <pre>
102 * GCRF
103 * |
104 * --------------------------------
105 * | | |
106 * Sun satellite Earth
107 * | |
108 * on-board antenna ground station
109 * |
110 * tracking antenna
111 * </pre>
112 * <p>Tracking measurements really correspond to the link between the ground
113 * and on-board antennas. This is tightly linked to the transform between
114 * these two frames, however neither frame is the direct parent frame of the
115 * other one: the path involves four intermediate frames. When we process a
116 * measurement, what we really want to update is the transform that defines
117 * the satellite frame with respect to its parent GCRF frame. This
118 * is the purpose of this method. This update is done by the following call,
119 * where <code>measurementTransform</code> represents the measurement as a
120 * simple translation transform between the two antenna frames:</p>
121 * <pre><code>
122 * satellite.updateTransform(onBoardAntenna, trackingAntenna,
123 * measurementTransform, date);
124 * </code></pre>
125 * <p>One way to represent the behavior of the method is to consider the
126 * sub-tree rooted at the instance on one hand (satellite and on-board antenna
127 * in the example above) and the tree containing all the other frames on the
128 * other hand (GCRF, Sun, Earth, ground station, tracking antenna).
129 * Both tree are considered as two solid sets linked together by a flexible
130 * spring, which is the transform we want to update. The method stretches the
131 * spring to make sure the transform between the two specified frames (one in
132 * each tree part) matches the specified transform.</p>
133 * @param f1 first control frame (may be the instance itself)
134 * @param f2 second control frame (may be the instance itself)
135 * @param f1Tof2 desired transform from first to second control frame
136 * @param date date of the transform
137 */
138 public void updateTransform(final Frame f1, final Frame f2, final Transform f1Tof2,
139 final AbsoluteDate date) {
140
141 Frame fA = f1;
142 Frame fB = f2;
143 Transform fAtoB = f1Tof2;
144
145 // make sure f1 is not a child of the instance
146 if (fA.isChildOf(this) || fA == this) {
147
148 if (fB.isChildOf(this) || fB == this) {
149 throw new FrameAncestorException(OrekitMessages.FRAME_ANCESTOR_OF_BOTH_FRAMES,
150 getName(), fA.getName(), fB.getName());
151 }
152
153 // swap f1 and f2 to make sure the child is f2
154 final Frame tmp = fA;
155 fA = fB;
156 fB = tmp;
157 fAtoB = fAtoB.getInverse();
158
159 } else if (!(fB.isChildOf(this) || fB == this)) {
160 throw new FrameAncestorException(OrekitMessages.FRAME_ANCESTOR_OF_NEITHER_FRAME,
161 getName(), fA.getName(), fB.getName());
162 }
163
164 // rebuild the transform by traveling from parent to self
165 // WITHOUT using the existing provider from parent to self that will be updated
166 final Transform parentTofA = getParent().getTransformTo(fA, date);
167 final Transform fBtoSelf = fB.getTransformTo(this, date);
168 final Transform fAtoSelf = new Transform(date, fAtoB, fBtoSelf);
169 final Transform parentToSelf = new Transform(date, parentTofA, fAtoSelf);
170
171 // update the existing provider from parent to self
172 ((UpdatableProvider) getTransformProvider()).setTransform(parentToSelf);
173
174 }
175
176 /** Local provider for transforms. */
177 private static class UpdatableProvider implements TransformProvider {
178
179 /** Current transform. */
180 private AtomicReference<Transform> transform;
181
182 /** Simple constructor.
183 * @param transform initial value of the transform
184 */
185 UpdatableProvider(final Transform transform) {
186 this.transform = new AtomicReference<>(transform);
187 }
188
189 /** Update the transform from the parent frame to the instance.
190 * @param transform new transform from parent frame to instance
191 */
192 public void setTransform(final Transform transform) {
193 this.transform.set(transform);
194 }
195
196 /** {@inheritDoc} */
197 public Transform getTransform(final AbsoluteDate date) {
198 return transform.get();
199 }
200
201 /** {@inheritDoc} */
202 @Override
203 public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
204 return new FieldTransform<>(date.getField(), transform.get());
205 }
206 }
207
208 }