1 /* Copyright 2025-2026 Hawkeye 360 (HE360)
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 java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.hipparchus.analysis.differentiation.Gradient;
24 import org.hipparchus.analysis.differentiation.GradientField;
25 import org.orekit.frames.FieldTransform;
26 import org.orekit.frames.Frame;
27 import org.orekit.frames.Transform;
28 import org.orekit.propagation.SpacecraftState;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.FieldAbsoluteDate;
31 import org.orekit.time.clocks.ClockOffset;
32 import org.orekit.time.clocks.FieldClockOffset;
33 import org.orekit.time.clocks.QuadraticFieldClockModel;
34 import org.orekit.utils.FieldPVCoordinatesProvider;
35 import org.orekit.utils.PVCoordinatesProvider;
36 import org.orekit.utils.ParameterDriver;
37 import org.orekit.utils.TimeSpanMap.Span;
38
39 /** Abstract interface that contains those methods necessary
40 * for both space and ground-based satellite observers.
41 *
42 * @author Brianna Aubin
43 * @since 14.0
44 */
45 public interface Observer extends MeasurementParticipant {
46
47 /** Get the type of object being used in measurement observations.
48 * @return boolean
49 */
50 boolean isSpaceBased();
51
52 /** Return the PVCoordinatesProvider.
53 * @return pos/vel coordinates provider
54 */
55 PVCoordinatesProvider getPVCoordinatesProvider();
56
57 /** Return the FieldPVCoordinatesProvider.
58 * @param freeParameters number of estimated parameters
59 * @param parameterIndices indices of the estimated parameters in derivatives computations, must be driver
60 * @return pos/vel coordinates provider for values with Gradient field
61 */
62 FieldPVCoordinatesProvider<Gradient> getFieldPVCoordinatesProvider(int freeParameters,
63 Map<String, Integer> parameterIndices);
64
65 /** Get the transform between offset frame and inertial frame.
66 * <p>
67 * The offset frame takes the <em>current</em> position offset,
68 * polar motion and the meridian shift into account. The frame
69 * returned is disconnected from later changes in the parameters.
70 * When the {@link ParameterDriver parameters} managing these
71 * offsets are changed, the method must be called again to retrieve
72 * a new offset frame.
73 * </p>
74 * @param inertial inertial frame to transform to
75 * @param date date of the transform
76 * @param clockOffsetAlreadyApplied if true, the specified {@code date} is as read
77 * by the ground station clock (i.e. clock offset <em>not</em> compensated), if false,
78 * the specified {@code date} was already compensated and is a physical absolute date
79 * @return transform between offset frame and inertial frame, at <em>real</em> measurement
80 * date (i.e. with clock, Earth and station offsets applied)
81 */
82 Transform getOffsetToInertial(Frame inertial, AbsoluteDate date, boolean clockOffsetAlreadyApplied);
83
84 /** Get the transform between offset frame and inertial frame with derivatives.
85 * <p>
86 * As the East and North vectors are not well defined at pole, the derivatives
87 * of these two vectors diverge to infinity as we get closer to the pole.
88 * So this method should not be used for stations less than 0.0001 degree from
89 * either poles.
90 * </p>
91 * @param inertial inertial frame to transform to
92 * @param clockDate date of the transform, clock offset and its derivatives already compensated
93 * @param freeParameters total number of free parameters in the gradient
94 * @param indices indices of the estimated parameters in derivatives computations, must be driver
95 * span name in map, not driver name or will not give right results (see {@link ParameterDriver#getValue(int, Map)})
96 * @return transform between offset frame and inertial frame, at specified date
97 */
98 default FieldTransform<Gradient> getOffsetToInertial(final Frame inertial,
99 final AbsoluteDate clockDate,
100 final int freeParameters,
101 final Map<String, Integer> indices) {
102 // take clock offset into account
103 final Gradient offset = getFieldOffsetValue(freeParameters, clockDate, indices);
104 final FieldAbsoluteDate<Gradient> offsetCompensatedDate = new FieldAbsoluteDate<>(clockDate, offset.negate());
105
106 return getOffsetToInertial(inertial, offsetCompensatedDate, freeParameters, indices);
107 }
108
109 /** Get the transform between offset frame and inertial frame with derivatives.
110 * <p>
111 * As the East and North vectors are not well defined at pole, the derivatives
112 * of these two vectors diverge to infinity as we get closer to the pole.
113 * So this method should not be used for stations less than 0.0001 degree from
114 * either poles.
115 * </p>
116 * @param inertial inertial frame to transform to
117 * @param offsetCompensatedDate date of the transform, clock offset and its derivatives already compensated
118 * @param freeParameters total number of free parameters in the gradient
119 * @param indices indices of the estimated parameters in derivatives computations, must be driver
120 * span name in map, not driver name or will not give right results (see {@link ParameterDriver#getValue(int, Map)})
121 * @return transform between offset frame and inertial frame, at specified date
122 */
123 FieldTransform<Gradient> getOffsetToInertial(Frame inertial, FieldAbsoluteDate<Gradient> offsetCompensatedDate,
124 int freeParameters, Map<String, Integer> indices);
125
126 /** Create a map of the free parameter values.
127 * @param states list of ObservableSatellite measurement states
128 * @param parameterDrivers list of all parameter values for the measurement
129 * @return map of the free parameter values
130 */
131 static Map<String, Integer> getParameterIndices(final SpacecraftState[] states,
132 final List<ParameterDriver> parameterDrivers) {
133
134 // measurement derivatives are computed with respect to spacecraft state in inertial frame
135 // Parameters:
136 // - 6k..6k+2 - Position of spacecraft k (counting k from 0 to nbSat-1) in inertial frame
137 // - 6k+3..6k+5 - Velocity of spacecraft k (counting k from 0 to nbSat-1) in inertial frame
138 // - 6nbSat..n - measurements parameters (clock offset, etc)
139 int nbParams = 6 * states.length;
140 final Map<String, Integer> paramIndices = new HashMap<>();
141 for (ParameterDriver measurementDriver : parameterDrivers) {
142 if (measurementDriver.isSelected()) {
143 for (Span<String> span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
144 paramIndices.put(span.getData(), nbParams++);
145 }
146 }
147 }
148 return paramIndices;
149 }
150
151 /**
152 * Compute actual date taking into account clock offset.
153 * @param date date as registered by observer
154 * @return corrected date
155 */
156 default AbsoluteDate getCorrectedReceptionDate(final AbsoluteDate date) {
157 final ClockOffset localClock = getQuadraticClockModel().getOffset(date);
158 return date.shiftedBy(-localClock.getBias());
159 }
160
161 /**
162 * Compute actual date taking into account clock offset.
163 * @param date date as registered by observer
164 * @param nbParams number of independent variables for automatic differentiation
165 * @param paramIndices mapping between parameter name and variable index
166 * @return corrected date
167 */
168 default FieldAbsoluteDate<Gradient> getCorrectedReceptionDateField(final AbsoluteDate date,
169 final int nbParams,
170 final Map<String, Integer> paramIndices) {
171 final QuadraticFieldClockModel<Gradient> quadraticClockModel = getQuadraticFieldClock(nbParams, date, paramIndices);
172 final GradientField field = GradientField.getField(nbParams);
173 final FieldAbsoluteDate<Gradient> fieldDate = new FieldAbsoluteDate<>(field, date);
174 final FieldClockOffset<Gradient> localClock = quadraticClockModel.getOffset(fieldDate);
175 return fieldDate.shiftedBy(localClock.getBias().negate());
176 }
177
178 }