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