1   /* Copyright 2022-2025 Luc Maisonobe
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.propagation.analytical.gnss;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
21  import org.hipparchus.util.MathArrays;
22  import org.orekit.propagation.FieldAdditionalDataProvider;
23  import org.orekit.propagation.FieldSpacecraftState;
24  import org.orekit.propagation.analytical.gnss.data.FieldGNSSClockElements;
25  import org.orekit.time.FieldAbsoluteDate;
26  import org.orekit.utils.Constants;
27  import org.orekit.utils.FieldPVCoordinates;
28  
29  /** Provider for clock corrections as additional states.
30   * <p>
31   * The value of this additional state is a three elements array containing
32   * </p>
33   * <ul>
34   *   <li>at index 0, the polynomial satellite clock model
35   *       Δtₛₐₜ = {@link FieldGNSSClockElements#getAf0() a₀} +
36   *               {@link FieldGNSSClockElements#getAf1() a₁} (t - {@link FieldGNSSClockElements#getToc() toc}) +
37   *               {@link FieldGNSSClockElements#getAf1() a₂} (t - {@link FieldGNSSClockElements#getToc() toc})²
38   *   </li>
39   *   <li>at index 1 the relativistic clock correction due to eccentricity</li>
40   *   <li>at index 2 the estimated group delay differential {@link FieldGNSSClockElements#getTGD() TGD} for L1-L2 correction</li>
41   * </ul>
42   *
43   * @param <T> type of the field elements
44   * @author Luc Maisonobe
45   * @since 13.0
46   */
47  public class FieldClockCorrectionsProvider<T extends CalculusFieldElement<T>>
48      implements FieldAdditionalDataProvider<T[], T> {
49  
50      /** The GPS clock elements. */
51      private final FieldGNSSClockElements<T> gnssClk;
52  
53      /** Clock reference epoch. */
54      private final FieldAbsoluteDate<T> clockRef;
55  
56      /** Duration of the GNSS cycle in seconds. */
57      private final double cycleDuration;
58  
59      /** Simple constructor.
60       * @param gnssClk GNSS clock elements
61       * @param cycleDuration duration of the GNSS cycle in seconds
62       */
63      public FieldClockCorrectionsProvider(final FieldGNSSClockElements<T> gnssClk,
64                                           final double cycleDuration) {
65          this.gnssClk       = gnssClk;
66          this.clockRef      = gnssClk.getDate();
67          this.cycleDuration = cycleDuration;
68      }
69  
70      /** {@inheritDoc} */
71      @Override
72      public String getName() {
73          return ClockCorrectionsProvider.CLOCK_CORRECTIONS;
74      }
75  
76      /**
77       * Get the duration from clock Reference epoch.
78       * <p>This takes the GNSS week roll-over into account.</p>
79       *
80       * @param date the considered date
81       * @return the duration from clock Reference epoch (s)
82       */
83      private T getDT(final FieldAbsoluteDate<T> date) {
84          // Time from ephemeris reference epoch
85          T dt = date.durationFrom(clockRef);
86          // Adjusts the time to take roll over week into account
87          while (dt.getReal() > 0.5 * cycleDuration) {
88              dt = dt.subtract(cycleDuration);
89          }
90          while (dt.getReal() < -0.5 * cycleDuration) {
91              dt = dt.add(cycleDuration);
92          }
93          // Returns the time from ephemeris reference epoch
94          return dt;
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public T[] getAdditionalData(final FieldSpacecraftState<T> state) {
100 
101         // polynomial clock model
102         final T  dt    = getDT(state.getDate());
103         final T  dtSat = gnssClk.getAf0().add(dt.multiply(gnssClk.getAf1().add(dt.multiply(gnssClk.getAf2()))));
104 
105         // relativistic effect due to eccentricity
106         final FieldPVCoordinates<T> pv    = state.getPVCoordinates();
107         final T                     dtRel = FieldVector3D.dotProduct(pv.getPosition(), pv.getVelocity()).
108                                             multiply(-2 / (Constants.SPEED_OF_LIGHT * Constants.SPEED_OF_LIGHT));
109 
110         // estimated group delay differential
111         final T tg = gnssClk.getTGD();
112 
113         final T[] array = MathArrays.buildArray(dt.getField(), 3);
114         array[0] = dtSat;
115         array[1] = dtRel;
116         array[2] = tg;
117         return array;
118     }
119 
120 }