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