1   /* Copyright 2022-2025 Thales Alenia Space
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.estimation.measurements;
18  
19  import org.hipparchus.analysis.differentiation.Gradient;
20  import org.hipparchus.analysis.differentiation.GradientField;
21  import org.hipparchus.util.Binary64;
22  import org.hipparchus.util.Binary64Field;
23  import org.hipparchus.util.FastMath;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.errors.OrekitException;
27  import org.orekit.errors.OrekitMessages;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.FieldAbsoluteDate;
30  import org.orekit.utils.ParameterDriver;
31  import org.orekit.utils.TimeSpanMap.Span;
32  
33  import java.util.HashMap;
34  import java.util.Map;
35  
36  public class QuadraticClockModelTest {
37  
38      @Test
39      public void testValue() {
40          final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
41          final QuadraticClockModel clock = new QuadraticClockModel(t0,
42                                                                    FastMath.scalb(1.0, -8),
43                                                                    FastMath.scalb(1.0, -9),
44                                                                    FastMath.scalb(1.0, -10));
45          Assertions.assertEquals(1.00 / 256.0, clock.getOffset(t0).getOffset(),                1.0e-15);
46          Assertions.assertEquals(1.75 / 256.0, clock.getOffset(t0.shiftedBy(1.0)).getOffset(), 1.0e-15);
47          Assertions.assertEquals(3.00 / 256.0, clock.getOffset(t0.shiftedBy(2.0)).getOffset(), 1.0e-15);
48      }
49  
50      @Test
51      public void testValueField() {
52          final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
53          final QuadraticClockModel clock = new QuadraticClockModel(t0,
54                                                                    FastMath.scalb(1.0, -8),
55                                                                    FastMath.scalb(1.0, -9),
56                                                                    FastMath.scalb(1.0, -10));
57          final FieldAbsoluteDate<Binary64> t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0);
58          Assertions.assertEquals(1.00 / 256.0, clock.getOffset(t064).getOffset().getReal(),                1.0e-15);
59          Assertions.assertEquals(1.75 / 256.0, clock.getOffset(t064.shiftedBy(1.0)).getOffset().getReal(), 1.0e-15);
60          Assertions.assertEquals(3.00 / 256.0, clock.getOffset(t064.shiftedBy(2.0)).getOffset().getReal(), 1.0e-15);
61      }
62  
63      @Test
64      public void testRate() {
65          final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
66          final QuadraticClockModel clock = new QuadraticClockModel(t0,
67                                                                    FastMath.scalb(1.0, -8),
68                                                                    FastMath.scalb(1.0, -9),
69                                                                    FastMath.scalb(1.0, -10));
70          Assertions.assertEquals(1.00 / 512, clock.getOffset(t0).getRate(),                1.0e-15);
71          Assertions.assertEquals(2.00 / 512, clock.getOffset(t0.shiftedBy(1.0)).getRate(), 1.0e-15);
72          Assertions.assertEquals(3.00 / 512, clock.getOffset(t0.shiftedBy(2.0)).getRate(), 1.0e-15);
73      }
74  
75      @Test
76      public void testRateField() {
77          final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
78          final QuadraticClockModel clock = new QuadraticClockModel(t0,
79                                                                    FastMath.scalb(1.0, -8),
80                                                                    FastMath.scalb(1.0, -9),
81                                                                    FastMath.scalb(1.0, -10));
82          final FieldAbsoluteDate<Binary64> t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0);
83          Assertions.assertEquals(1.00 / 512, clock.getOffset(t064).getRate().getReal(),                1.0e-15);
84          Assertions.assertEquals(2.00 / 512, clock.getOffset(t064.shiftedBy(1.0)).getRate().getReal(), 1.0e-15);
85          Assertions.assertEquals(3.00 / 512, clock.getOffset(t064.shiftedBy(2.0)).getRate().getReal(), 1.0e-15);
86      }
87  
88      @Test
89      public void testAcceleration() {
90          final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
91          final QuadraticClockModel clock = new QuadraticClockModel(t0,
92                                                                    FastMath.scalb(1.0, -8),
93                                                                    FastMath.scalb(1.0, -9),
94                                                                    FastMath.scalb(1.0, -10));
95          Assertions.assertEquals(2.00 / 1024, clock.getOffset(t0).getAcceleration(),                1.0e-15);
96          Assertions.assertEquals(2.00 / 1024, clock.getOffset(t0.shiftedBy(1.0)).getAcceleration(), 1.0e-15);
97          Assertions.assertEquals(2.00 / 1024, clock.getOffset(t0.shiftedBy(2.0)).getAcceleration(), 1.0e-15);
98      }
99  
100     @Test
101     public void testAccelerationField() {
102         final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
103         final QuadraticClockModel clock = new QuadraticClockModel(t0,
104                                                                   FastMath.scalb(1.0, -8),
105                                                                   FastMath.scalb(1.0, -9),
106                                                                   FastMath.scalb(1.0, -10));
107         final FieldAbsoluteDate<Binary64> t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0);
108         Assertions.assertEquals(2.00 / 1024, clock.getOffset(t064).getAcceleration().getReal(),                1.0e-15);
109         Assertions.assertEquals(2.00 / 1024, clock.getOffset(t064.shiftedBy(1.0)).getAcceleration().getReal(), 1.0e-15);
110         Assertions.assertEquals(2.00 / 1024, clock.getOffset(t064.shiftedBy(2.0)).getAcceleration().getReal(), 1.0e-15);
111     }
112 
113     @Test
114     public void testValidity() {
115         final AbsoluteDate        t0    = AbsoluteDate.GALILEO_EPOCH;
116         final QuadraticClockModel clock = new QuadraticClockModel(t0,
117                                                                   FastMath.scalb(1.0, -8),
118                                                                   FastMath.scalb(1.0, -9),
119                                                                   FastMath.scalb(1.0, -10));
120         Assertions.assertEquals(AbsoluteDate.PAST_INFINITY, clock.getValidityStart());
121         Assertions.assertEquals(AbsoluteDate.FUTURE_INFINITY, clock.getValidityEnd());
122     }
123 
124     @Test
125     public void testSafeReferenceDate() {
126         final ParameterDriver a0 = new ParameterDriver("a0", 0.0, 1.0,
127                                                        Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
128         final ParameterDriver a1 = new ParameterDriver("a1", 0.0, 1.0,
129                                                        Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
130         final ParameterDriver a2 = new ParameterDriver("a2", 0.0, 1.0,
131                                                        Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
132         final QuadraticClockModel clock = new QuadraticClockModel(a0, a1, a2);
133 
134         // OK to have no reference date if a1 and a2 are both zero
135         a0.setValue(0.125);
136         Assertions.assertEquals(0.125, clock.getOffset(AbsoluteDate.GALILEO_EPOCH).getOffset());
137 
138         // not OK to have no reference date if a1 is non zero
139         a1.setValue(1.0);
140         try {
141             clock.getOffset(AbsoluteDate.GALILEO_EPOCH);
142             Assertions.fail("an exception should have been thrown");
143         } catch (OrekitException oe) {
144             Assertions.assertEquals(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, oe.getSpecifier());
145             Assertions.assertEquals(a0.getName(), oe.getParts()[0]);
146         }
147 
148         // not OK to have no reference date if a2 is non zero
149         a1.setValue(0.0);
150         a2.setValue(1.0);
151         try {
152             clock.getOffset(AbsoluteDate.GALILEO_EPOCH);
153             Assertions.fail("an exception should have been thrown");
154         } catch (OrekitException oe) {
155             Assertions.assertEquals(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, oe.getSpecifier());
156             Assertions.assertEquals(a0.getName(), oe.getParts()[0]);
157         }
158 
159         // back to OK if we reset drift and acceleration
160         a2.setValue(0);
161         Assertions.assertEquals(0.125, clock.getOffset(AbsoluteDate.GALILEO_EPOCH).getOffset());
162 
163     }
164 
165     @Test
166     public void testGradient() {
167         final AbsoluteDate    t0 = AbsoluteDate.GALILEO_EPOCH;
168         final ParameterDriver a0 = new ParameterDriver("a0", 0.0, 1.0,
169                                                        Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
170         final ParameterDriver a1 = new ParameterDriver("a1", 0.0, 1.0,
171                                                        Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
172         final ParameterDriver a2 = new ParameterDriver("a2", 0.0, 1.0,
173                                                        Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
174         final QuadraticClockModel clock = new QuadraticClockModel(a0, a1, a2);
175 
176         int nbParams = 0;
177         final Map<String, Integer> indices = new HashMap<>();
178         a0.setValue(FastMath.scalb(1.0, -8));
179         a0.setReferenceDate(t0);
180         a0.setSelected(true);
181         for (Span<String> span = a0.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
182             indices.put(span.getData(), nbParams++);
183         }
184         a1.setValue(FastMath.scalb(1.0, -9));
185         a1.setReferenceDate(t0);
186         a1.setSelected(true);
187         for (Span<String> span = a1.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
188             indices.put(span.getData(), nbParams++);
189         }
190         a2.setValue(FastMath.scalb(1.0, -10));
191         a2.setReferenceDate(t0);
192         a2.setSelected(true);
193         for (Span<String> span = a2.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
194             indices.put(span.getData(), nbParams++);
195         }
196 
197         QuadraticFieldClockModel<Gradient> gradientModel = clock.toGradientModel(nbParams, indices, t0);
198         final FieldAbsoluteDate<Gradient> t0g = new FieldAbsoluteDate<>(GradientField.getField(nbParams), t0);
199 
200         final Gradient g0 = gradientModel.getOffset(t0g).getOffset();
201         Assertions.assertEquals(1.00 / 256, g0.getValue(), 1.0e-15);
202         Assertions.assertEquals(1.00,       g0.getPartialDerivative(0), 1.0e-15);
203         Assertions.assertEquals(0.00,       g0.getPartialDerivative(1), 1.0e-15);
204         Assertions.assertEquals(0.00,       g0.getPartialDerivative(2), 1.0e-15);
205 
206         final Gradient g1 = gradientModel.getOffset(t0g.shiftedBy(1.0)).getOffset();
207         Assertions.assertEquals(1.75 / 256, g1.getValue(), 1.0e-15);
208         Assertions.assertEquals(1.00,       g1.getPartialDerivative(0), 1.0e-15);
209         Assertions.assertEquals(1.00,       g1.getPartialDerivative(1), 1.0e-15);
210         Assertions.assertEquals(1.00,       g1.getPartialDerivative(2), 1.0e-15);
211 
212         final Gradient g2 = gradientModel.getOffset(t0g.shiftedBy(2.0)).getOffset();
213         Assertions.assertEquals(3.00 / 256, g2.getValue(), 1.0e-15);
214         Assertions.assertEquals(1.00,       g2.getPartialDerivative(0), 1.0e-15);
215         Assertions.assertEquals(2.00,       g2.getPartialDerivative(1), 1.0e-15);
216         Assertions.assertEquals(4.00,       g2.getPartialDerivative(2), 1.0e-15);
217 
218     }
219 
220 }