UpdatableFrame.java
/* Copyright 2002-2024 CS GROUP
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.frames;
import java.util.concurrent.atomic.AtomicReference;
import org.hipparchus.CalculusFieldElement;
import org.orekit.errors.FrameAncestorException;
import org.orekit.errors.OrekitMessages;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.FieldAbsoluteDate;
/** Frame whose transform from its parent can be updated.
* <p>This class allows to control the relative position of two parts
* of the global frames tree using any two frames in each part as
* control handles. Consider the following simplified frames tree as an
* example:</p>
* <pre>
* GCRF
* |
* --------------------------------
* | | |
* Sun satellite Earth
* | |
* on-board antenna ground station
* |
* tracking antenna
* </pre>
* <p>Tracking measurements really correspond to the link between the ground
* and on-board antennas. This is tightly linked to the transform between
* these two frames, however neither frame is the direct parent frame of the
* other one: the path involves four intermediate frames. When we process a
* measurement, what we really want to update is the transform that defines
* the satellite frame with respect to its parent GCRF frame.</p>
* <p>In order to implement the above case, the satellite frame is defined
* as an instance of this class and its {@link #updateTransform(Frame, Frame,
* Transform, AbsoluteDate) updateTransform} would be called each time we want
* to adjust the frame, i.e. each time we get a new measurement between the
* two antennas.</p>
* @author Luc Maisonobe
*/
public class UpdatableFrame extends Frame {
/** Serializable UID. */
private static final long serialVersionUID = -2075893064211339303L;
/** Build a non-inertial frame from its transform with respect to its parent.
* <p>calling this constructor is equivalent to call
* {@link #UpdatableFrame(Frame, Transform, String, boolean)
* UpdatableFrame(parent, transform, name, false)}.</p>
* @param parent parent frame (must be non-null)
* @param transform transform from parent frame to instance
* @param name name of the frame
* @exception IllegalArgumentException if the parent frame is null
*/
public UpdatableFrame(final Frame parent, final Transform transform, final String name)
throws IllegalArgumentException {
this(parent, transform, name, false);
}
/** Build a frame from its transform with respect to its parent.
* <p>The convention for the transform is that it is from parent
* frame to instance. This means that the two following frames
* are similar:</p>
* <pre>
* Frame frame1 = new Frame(FramesFactory.getGCRF(), new Transform(t1, t2));
* Frame frame2 = new Frame(new Frame(FramesFactory.getGCRF(), t1), t2);
* </pre>
* @param parent parent frame (must be non-null)
* @param transform transform from parent frame to instance
* @param name name of the frame
* @param pseudoInertial true if frame is considered pseudo-inertial
* (i.e. suitable for propagating orbit)
* @exception IllegalArgumentException if the parent frame is null
*/
public UpdatableFrame(final Frame parent, final Transform transform, final String name,
final boolean pseudoInertial)
throws IllegalArgumentException {
super(parent, new UpdatableProvider(transform), name, pseudoInertial);
}
/** Update the transform from parent frame implicitly according to two other
* frames.
* <p>This method allows to control the relative position of two parts
* of the global frames tree using any two frames in each part as
* control handles. Consider the following simplified frames tree as an
* example:</p>
* <pre>
* GCRF
* |
* --------------------------------
* | | |
* Sun satellite Earth
* | |
* on-board antenna ground station
* |
* tracking antenna
* </pre>
* <p>Tracking measurements really correspond to the link between the ground
* and on-board antennas. This is tightly linked to the transform between
* these two frames, however neither frame is the direct parent frame of the
* other one: the path involves four intermediate frames. When we process a
* measurement, what we really want to update is the transform that defines
* the satellite frame with respect to its parent GCRF frame. This
* is the purpose of this method. This update is done by the following call,
* where <code>measurementTransform</code> represents the measurement as a
* simple translation transform between the two antenna frames:</p>
* <pre><code>
* satellite.updateTransform(onBoardAntenna, trackingAntenna,
* measurementTransform, date);
* </code></pre>
* <p>One way to represent the behavior of the method is to consider the
* sub-tree rooted at the instance on one hand (satellite and on-board antenna
* in the example above) and the tree containing all the other frames on the
* other hand (GCRF, Sun, Earth, ground station, tracking antenna).
* Both tree are considered as two solid sets linked together by a flexible
* spring, which is the transform we want to update. The method stretches the
* spring to make sure the transform between the two specified frames (one in
* each tree part) matches the specified transform.</p>
* @param f1 first control frame (may be the instance itself)
* @param f2 second control frame (may be the instance itself)
* @param f1Tof2 desired transform from first to second control frame
* @param date date of the transform
*/
public void updateTransform(final Frame f1, final Frame f2, final Transform f1Tof2,
final AbsoluteDate date) {
Frame fA = f1;
Frame fB = f2;
Transform fAtoB = f1Tof2;
// make sure f1 is not a child of the instance
if (fA.isChildOf(this) || fA == this) {
if (fB.isChildOf(this) || fB == this) {
throw new FrameAncestorException(OrekitMessages.FRAME_ANCESTOR_OF_BOTH_FRAMES,
getName(), fA.getName(), fB.getName());
}
// swap f1 and f2 to make sure the child is f2
final Frame tmp = fA;
fA = fB;
fB = tmp;
fAtoB = fAtoB.getInverse();
} else if (!(fB.isChildOf(this) || fB == this)) {
throw new FrameAncestorException(OrekitMessages.FRAME_ANCESTOR_OF_NEITHER_FRAME,
getName(), fA.getName(), fB.getName());
}
// rebuild the transform by traveling from parent to self
// WITHOUT using the existing provider from parent to self that will be updated
final Transform parentTofA = getParent().getTransformTo(fA, date);
final Transform fBtoSelf = fB.getTransformTo(this, date);
final Transform fAtoSelf = new Transform(date, fAtoB, fBtoSelf);
final Transform parentToSelf = new Transform(date, parentTofA, fAtoSelf);
// update the existing provider from parent to self
((UpdatableProvider) getTransformProvider()).setTransform(parentToSelf);
}
/** Local provider for transforms. */
private static class UpdatableProvider implements TransformProvider {
/** Serializable UID. */
private static final long serialVersionUID = 4436954500689776331L;
/** Current transform. */
private AtomicReference<Transform> transform;
/** Simple constructor.
* @param transform initial value of the transform
*/
UpdatableProvider(final Transform transform) {
this.transform = new AtomicReference<Transform>(transform);
}
/** Update the transform from the parent frame to the instance.
* @param transform new transform from parent frame to instance
*/
public void setTransform(final Transform transform) {
this.transform.set(transform);
}
/** {@inheritDoc} */
public Transform getTransform(final AbsoluteDate date) {
return transform.get();
}
/** {@inheritDoc} */
@Override
public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date) {
return new FieldTransform<>(date.getField(), transform.get());
}
}
}