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 }