1   /* Copyright 2013-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.rugged.los;
18  
19  import java.util.stream.Stream;
20  
21  import org.hipparchus.analysis.differentiation.Derivative;
22  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
23  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24  import org.hipparchus.geometry.euclidean.threed.Rotation;
25  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.hipparchus.util.FastMath;
28  import org.orekit.rugged.utils.DerivativeGenerator;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.utils.ParameterDriver;
31  import org.orekit.utils.ParameterObserver;
32  import org.orekit.utils.TimeSpanMap;
33  
34  /** {@link TimeIndependentLOSTransform LOS transform} based on a fixed rotation.
35   * @author Luc Maisonobe
36   * @see LOSBuilder
37   */
38  public class FixedRotation implements TimeIndependentLOSTransform {
39  
40      /** Parameters scaling factor.
41       * <p>
42       * We use a power of 2 to avoid numeric noise introduction
43       * in the multiplications/divisions sequences.
44       * </p>
45       */
46      private final double SCALE = FastMath.scalb(1.0, -20);
47  
48      /** Rotation axis. */
49      private final Vector3D axis;
50  
51      /** Underlying rotation. */
52      private Rotation rotation;
53  
54      /** Underlying rotation with derivatives. */
55      private FieldRotation<?> rDS;
56  
57      /** Driver for rotation angle. */
58      private final ParameterDriver angleDriver;
59  
60      /** Simple constructor.
61       * <p>
62       * The single parameter is the rotation angle.
63       * </p>
64       * @param name name of the rotation (used for estimated parameters identification)
65       * @param axis rotation axis
66       * @param angle rotation angle
67       */
68      public FixedRotation(final String name, final Vector3D axis, final double angle) {
69  
70          this.axis     = axis;
71          this.rotation = null;
72          this.rDS      = null;
73          this.angleDriver = new ParameterDriver(name, angle, SCALE, -2 * FastMath.PI, 2 * FastMath.PI);
74          angleDriver.addObserver(new ParameterObserver() {
75              @Override
76              public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
77                  // reset rotations to null, they will be evaluated lazily if needed
78                  rotation = null;
79                  rDS      = null;
80              }
81  
82              @Override
83              public void valueSpanMapChanged(final TimeSpanMap<Double> previousValueSpanMap, final ParameterDriver driver) {
84                  // reset rotations to null, they will be evaluated lazily if needed
85                  rotation = null;
86                  rDS      = null;
87              }
88          });
89      }
90  
91      /** {@inheritDoc} */
92      @Override
93      public Stream<ParameterDriver> getParametersDrivers() {
94          return Stream.of(angleDriver);
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public Vector3D transformLOS(final int i, final Vector3D los) {
100         if (rotation == null) {
101             // lazy evaluation of the rotation
102             rotation = new Rotation(axis, angleDriver.getValue(), RotationConvention.VECTOR_OPERATOR);
103         }
104         return rotation.applyTo(los);
105     }
106 
107     /** {@inheritDoc} */
108     @SuppressWarnings("unchecked")
109     @Override
110     public <T extends Derivative<T>> FieldVector3D<T> transformLOS(final int i, final FieldVector3D<T> los,
111                                                                    final DerivativeGenerator<T> generator) {
112         final FieldRotation<T> rD;
113         if (rDS == null || !rDS.getQ0().getField().equals(generator.getField())) {
114 
115             // lazy evaluation of the rotation
116             final FieldVector3D<T> axisDS =
117                             new FieldVector3D<>(generator.constant(axis.getX()),
118                                                 generator.constant(axis.getY()),
119                                                 generator.constant(axis.getZ()));
120             final T angleDS = generator.variable(angleDriver);
121             rD = new FieldRotation<>(axisDS, angleDS, RotationConvention.VECTOR_OPERATOR);
122 
123             // cache evaluated rotation
124             rDS = rD;
125 
126         } else {
127             // reuse cached value
128             rD  = (FieldRotation<T>) rDS;
129         }
130 
131         return rD.applyTo(los);
132 
133     }
134 
135 }