1   /* Copyright 2002-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.time;
18  
19  import org.hipparchus.CalculusFieldElement;
20  
21  /** Scale for on-board clock.
22   * @author Luc Maisonobe
23   * @since 11.0
24   */
25  public class SatelliteClockScale implements TimeScale {
26  
27      /** Name of the scale. */
28      private final String name;
29  
30      /** Reference epoch. */
31      private final AbsoluteDate epoch;
32  
33      /** Reference epoch. */
34      private final DateTimeComponents epochDT;
35  
36      /** Offset from TAI at epoch. */
37      private final TimeOffset offsetAtEpoch;
38  
39      /** Clock count at epoch. */
40      private final double countAtEpoch;
41  
42      /** Clock drift (i.e. clock count per SI second minus 1.0). */
43      private final double drift;
44  
45      /** Clock rate. */
46      private final double rate;
47  
48      /** Create a linear model for satellite clock.
49       * <p>
50       * Beware that we specify the model using its drift with respect to
51       * flow of time. For a perfect clock without any drift, the clock
52       * count would be one tick every SI second. A clock that is fast, say
53       * for example it generates 1000001 ticks every 1000000 SI second, would
54       * have a rate of 1.000001 tick per SI second and hence a drift of
55       * 1.0e-6 tick per SI second. In this constructor we use the drift
56       * (1.0e-6 in the previous example) rather than the rate (1.000001
57       * in the previous example) to specify the clock. The rationale is
58       * that for clocks that are intended to be used for representing absolute
59       * time, the drift is expected to be small (much smaller that 1.0e-6
60       * for a good clock), so using drift is numerically more stable than
61       * using rate and risking catastrophic cancellation when subtracting
62       * 1.0 in the internal computation.
63       * </p>
64       * <p>
65       * Despite what is explained in the previous paragraph, this class can
66       * handle spacecraft clocks that are not intended to be synchronized with
67       * SI seconds, for example clocks that ticks at 10 Hz. In such cases the
68       * drift would need to be set at 10.0 - 1.0 = 9.0, which is not intuitive.
69       * For these clocks, the methods {@link #countAtDate(AbsoluteDate)} and
70       * {@link #dateAtCount(double)} and perhaps {@link #offsetFromTAI(AbsoluteDate)}
71       * are still useful, whereas {@link #offsetToTAI(DateComponents, TimeComponents)}
72       * is probably not really meaningful.
73       * </p>
74       * @param name of the scale
75       * @param epoch reference epoch
76       * @param epochScale time scale in which the {@code epoch} was defined
77       * @param countAtEpoch clock count at {@code epoch}
78       * @param drift clock drift rate (i.e. clock count change per SI second minus 1.0)
79       */
80      public SatelliteClockScale(final String name,
81                                 final AbsoluteDate epoch, final TimeScale epochScale,
82                                 final double countAtEpoch, final double drift) {
83          this.name          = name;
84          this.epoch         = epoch;
85          this.epochDT       = epoch.getComponents(epochScale);
86          this.offsetAtEpoch = epochScale.offsetFromTAI(epoch).add(new TimeOffset(countAtEpoch));
87          this.countAtEpoch  = countAtEpoch;
88          this.drift         = drift;
89          this.rate          = 1.0 + drift;
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public TimeOffset offsetFromTAI(final AbsoluteDate date) {
95          return offsetAtEpoch.add(new TimeOffset(drift * date.durationFrom(epoch)));
96      }
97  
98      /** {@inheritDoc} */
99      @Override
100     public TimeOffset offsetToTAI(final DateComponents date, final TimeComponents time) {
101         final long      deltaDate      = (date.getJ2000Day() - epochDT.getDate().getJ2000Day()) *
102                                          TimeOffset.DAY.getSeconds();
103         final TimeOffset deltaTime      = time.getSplitSecondsInUTCDay().
104                                          subtract(epochDT.getTime().getSplitSecondsInUTCDay());
105         final double    delta          = deltaDate + deltaTime.toDouble();
106         final double    timeSinceEpoch = (delta - countAtEpoch) / rate;
107         return offsetAtEpoch.add(new TimeOffset(drift * timeSinceEpoch)).negate();
108     }
109 
110     /** Compute date corresponding to some clock count.
111      * @param count clock count
112      * @return date at {@code count}
113      */
114     public AbsoluteDate dateAtCount(final double count) {
115         return epoch.shiftedBy((count - countAtEpoch) / rate);
116     }
117 
118     /** Compute clock count corresponding to some date.
119      * @param date date
120      * @return clock count at {@code date}
121      */
122     public double countAtDate(final AbsoluteDate date) {
123         return countAtEpoch + rate * date.durationFrom(epoch);
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public <T extends CalculusFieldElement<T>> T offsetFromTAI(final FieldAbsoluteDate<T> date) {
129         return date.durationFrom(epoch).multiply(drift).add(offsetAtEpoch.toDouble());
130     }
131 
132     /** {@inheritDoc} */
133     public String getName() {
134         return name;
135     }
136 
137     /** {@inheritDoc} */
138     public String toString() {
139         return getName();
140     }
141 
142 }