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.estimation.measurements;
18  
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import org.hipparchus.analysis.differentiation.Gradient;
24  import org.hipparchus.analysis.differentiation.GradientField;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.orekit.frames.FieldTransform;
28  import org.orekit.frames.Frame;
29  import org.orekit.frames.Transform;
30  import org.orekit.propagation.SpacecraftState;
31  import org.orekit.time.AbsoluteDate;
32  import org.orekit.time.FieldAbsoluteDate;
33  import org.orekit.utils.PVCoordinates;
34  import org.orekit.utils.ParameterDriver;
35  import org.orekit.utils.TimeSpanMap.Span;
36  import org.orekit.utils.TimeStampedFieldPVCoordinates;
37  import org.orekit.utils.TimeStampedPVCoordinates;
38  
39  /** Base class modeling a measurement where receiver is a ground station.
40   * @author Thierry Ceolin
41   * @author Luc Maisonobe
42   * @author Maxime Journot
43   * @since 12.0
44   * @param <T> type of the measurement
45   */
46  public abstract class GroundReceiverMeasurement<T extends GroundReceiverMeasurement<T>> extends AbstractMeasurement<T> {
47  
48      /** Ground station from which measurement is performed. */
49      private final GroundStation station;
50  
51      /** Flag indicating whether it is a two-way measurement. */
52      private final boolean twoway;
53  
54      /** Simple constructor.
55       * @param station ground station from which measurement is performed
56       * @param twoWay flag indicating whether it is a two-way measurement
57       * @param date date of the measurement
58       * @param observed observed value
59       * @param sigma theoretical standard deviation
60       * @param baseWeight base weight
61       * @param satellite satellite related to this measurement
62       */
63      public GroundReceiverMeasurement(final GroundStation station, final boolean twoWay, final AbsoluteDate date,
64                                       final double observed, final double sigma, final double baseWeight,
65                                       final ObservableSatellite satellite) {
66          super(date, observed, sigma, baseWeight, Collections.singletonList(satellite));
67          addParameterDriver(station.getClockOffsetDriver());
68          addParameterDriver(station.getClockDriftDriver());
69          addParameterDriver(station.getClockAccelerationDriver());
70          addParameterDriver(station.getEastOffsetDriver());
71          addParameterDriver(station.getNorthOffsetDriver());
72          addParameterDriver(station.getZenithOffsetDriver());
73          addParameterDriver(station.getPrimeMeridianOffsetDriver());
74          addParameterDriver(station.getPrimeMeridianDriftDriver());
75          addParameterDriver(station.getPolarOffsetXDriver());
76          addParameterDriver(station.getPolarDriftXDriver());
77          addParameterDriver(station.getPolarOffsetYDriver());
78          addParameterDriver(station.getPolarDriftYDriver());
79          if (!twoWay) {
80              // for one way measurements, the satellite clock offset affects the measurement
81              addParameterDriver(satellite.getClockOffsetDriver());
82              addParameterDriver(satellite.getClockDriftDriver());
83              addParameterDriver(satellite.getClockAccelerationDriver());
84          }
85          this.station = station;
86          this.twoway  = twoWay;
87      }
88  
89      /** Simple constructor.
90       * @param station ground station from which measurement is performed
91       * @param twoWay flag indicating whether it is a two-way measurement
92       * @param date date of the measurement
93       * @param observed observed value
94       * @param sigma theoretical standard deviation
95       * @param baseWeight base weight
96       * @param satellite satellite related to this measurement
97       */
98      public GroundReceiverMeasurement(final GroundStation station, final boolean twoWay, final AbsoluteDate date,
99                                       final double[] observed, final double[] sigma, final double[] baseWeight,
100                                      final ObservableSatellite satellite) {
101         super(date, observed, sigma, baseWeight, Collections.singletonList(satellite));
102         addParameterDriver(station.getClockOffsetDriver());
103         addParameterDriver(station.getClockDriftDriver());
104         addParameterDriver(station.getClockAccelerationDriver());
105         addParameterDriver(station.getEastOffsetDriver());
106         addParameterDriver(station.getNorthOffsetDriver());
107         addParameterDriver(station.getZenithOffsetDriver());
108         addParameterDriver(station.getPrimeMeridianOffsetDriver());
109         addParameterDriver(station.getPrimeMeridianDriftDriver());
110         addParameterDriver(station.getPolarOffsetXDriver());
111         addParameterDriver(station.getPolarDriftXDriver());
112         addParameterDriver(station.getPolarOffsetYDriver());
113         addParameterDriver(station.getPolarDriftYDriver());
114         if (!twoWay) {
115             // for one way measurements, the satellite clock offset affects the measurement
116             addParameterDriver(satellite.getClockOffsetDriver());
117             addParameterDriver(satellite.getClockDriftDriver());
118             addParameterDriver(satellite.getClockAccelerationDriver());
119         }
120         this.station = station;
121         this.twoway  = twoWay;
122     }
123 
124     /** Get the ground station from which measurement is performed.
125      * @return ground station from which measurement is performed
126      */
127     public GroundStation getStation() {
128         return station;
129     }
130 
131     /** Check if the instance represents a two-way measurement.
132      * @return true if the instance represents a two-way measurement
133      */
134     public boolean isTwoWay() {
135         return twoway;
136     }
137 
138     /** Compute common estimation parameters.
139      * @param state orbital state at measurement date
140      * @return common parameters
141      */
142     protected GroundReceiverCommonParametersWithoutDerivatives computeCommonParametersWithout(final SpacecraftState state) {
143 
144         // Coordinates of the spacecraft
145         final TimeStampedPVCoordinates pva = state.getPVCoordinates();
146 
147         // transform between station and inertial frame
148         final Transform offsetToInertialDownlink =
149                         getStation().getOffsetToInertial(state.getFrame(), getDate(), false);
150         final AbsoluteDate downlinkDate = offsetToInertialDownlink.getDate();
151 
152         // Station position in inertial frame at end of the downlink leg
153         final TimeStampedPVCoordinates origin = new TimeStampedPVCoordinates(downlinkDate,
154                                                                              Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO);
155         final TimeStampedPVCoordinates stationDownlink = offsetToInertialDownlink.transformPVCoordinates(origin);
156 
157         // Compute propagation times
158         // (if state has already been set up to pre-compensate propagation delay,
159         //  we will have delta == tauD and transitState will be the same as state)
160 
161         // Downlink delay
162         final double tauD = signalTimeOfFlightAdjustableEmitter(pva, stationDownlink.getPosition(), downlinkDate, state.getFrame());
163 
164         // Transit state & Transit state (re)computed with gradients
165         final double          delta        = downlinkDate.durationFrom(state.getDate());
166         final double          deltaMTauD   = delta - tauD;
167         final SpacecraftState transitState = state.shiftedBy(deltaMTauD);
168 
169         return new GroundReceiverCommonParametersWithoutDerivatives(state,
170                                                                     offsetToInertialDownlink,
171                                                                     stationDownlink,
172                                                                     tauD,
173                                                                     transitState,
174                                                                     transitState.getPVCoordinates());
175 
176     }
177 
178     /** Compute common estimation parameters.
179      * @param state orbital state at measurement date
180      * @return common parameters
181      */
182     protected GroundReceiverCommonParametersWithDerivatives computeCommonParametersWithDerivatives(final SpacecraftState state) {
183         int nbParams = 6;
184         final Map<String, Integer> indices = new HashMap<>();
185         for (ParameterDriver driver : getParametersDrivers()) {
186             if (driver.isSelected()) {
187                 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
188                     indices.put(span.getData(), nbParams++);
189                 }
190             }
191         }
192         final FieldVector3D<Gradient> zero = FieldVector3D.getZero(GradientField.getField(nbParams));
193 
194         // Coordinates of the spacecraft expressed as a gradient
195         final TimeStampedFieldPVCoordinates<Gradient> pva = getCoordinates(state, 0, nbParams);
196 
197         // transform between station and inertial frame, expressed as a gradient
198         // The components of station's position in offset frame are the 3 last derivative parameters
199         final FieldTransform<Gradient> offsetToInertialDownlink =
200                         getStation().getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
201         final FieldAbsoluteDate<Gradient> downlinkDate = offsetToInertialDownlink.getFieldDate();
202 
203         // Station position in inertial frame at end of the downlink leg
204         final TimeStampedFieldPVCoordinates<Gradient> stationDownlink =
205                         offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDate,
206                                                                                                             zero, zero, zero));
207 
208         // Compute propagation times
209         // (if state has already been set up to pre-compensate propagation delay,
210         //  we will have delta == tauD and transitState will be the same as state)
211 
212         // Downlink delay
213         final Gradient tauD = signalTimeOfFlightAdjustableEmitter(pva, stationDownlink.getPosition(),
214                                                                   downlinkDate, state.getFrame());
215 
216         // Transit state & Transit state (re)computed with gradients
217         final Gradient        delta        = downlinkDate.durationFrom(state.getDate());
218         final Gradient        deltaMTauD   = tauD.negate().add(delta);
219         final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
220         final TimeStampedFieldPVCoordinates<Gradient> transitPV = pva.shiftedBy(deltaMTauD);
221 
222         return new GroundReceiverCommonParametersWithDerivatives(state,
223                                                                  indices,
224                                                                  offsetToInertialDownlink,
225                                                                  stationDownlink,
226                                                                  tauD,
227                                                                  transitState,
228                                                                  transitPV);
229 
230     }
231 
232     /**
233      * Get the station position for a given frame.
234      * @param frame inertial frame for station position
235      * @return the station position in the given inertial frame
236      * @since 12.0
237      */
238     public Vector3D getGroundStationPosition(final Frame frame) {
239         return station.getBaseFrame().getPosition(getDate(), frame);
240     }
241 
242     /**
243      * Get the station coordinates for a given frame.
244      * @param frame inertial frame for station position
245      * @return the station coordinates in the given inertial frame
246      * @since 12.0
247      */
248     public PVCoordinates getGroundStationCoordinates(final Frame frame) {
249         return station.getBaseFrame().getPVCoordinates(getDate(), frame);
250     }
251 
252 }