1   /* Copyright 2002-2024 Mark Rutten
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    * Mark Rutten 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.Arrays;
20  
21  import org.hipparchus.analysis.differentiation.Gradient;
22  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.orekit.frames.FieldTransform;
25  import org.orekit.frames.Transform;
26  import org.orekit.propagation.SpacecraftState;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.time.FieldAbsoluteDate;
29  import org.orekit.utils.Constants;
30  import org.orekit.utils.ParameterDriver;
31  import org.orekit.utils.TimeSpanMap.Span;
32  import org.orekit.utils.TimeStampedFieldPVCoordinates;
33  import org.orekit.utils.TimeStampedPVCoordinates;
34  
35  /**
36   * Class modeling a bistatic range measurement using
37   * an emitter ground station and a receiver ground station.
38   * <p>
39   * The measurement is considered to be a signal:
40   * <ul>
41   * <li>Emitted from the emitter ground station</li>
42   * <li>Reflected on the spacecraft</li>
43   * <li>Received on the receiver ground station</li>
44   * </ul>
45   * The date of the measurement corresponds to the reception on ground of the reflected signal.
46   * <p>
47   * The motion of the stations and the spacecraft during the signal flight time are taken into account.
48   * </p>
49   *
50   * @author Mark Rutten
51   * @since 11.2
52   */
53  public class BistaticRange extends GroundReceiverMeasurement<BistaticRange> {
54  
55      /** Type of the measurement. */
56      public static final String MEASUREMENT_TYPE = "BistaticRange";
57  
58      /**
59       * Ground station from which transmission is made.
60       */
61      private final GroundStation emitter;
62  
63      /**
64       * Simple constructor.
65       *
66       * @param emitter     ground station from which transmission is performed
67       * @param receiver    ground station from which measurement is performed
68       * @param date        date of the measurement
69       * @param range       observed value
70       * @param sigma       theoretical standard deviation
71       * @param baseWeight  base weight
72       * @param satellite   satellite related to this measurement
73       * @since 11.2
74       */
75      public BistaticRange(final GroundStation emitter, final GroundStation receiver, final AbsoluteDate date,
76                           final double range, final double sigma, final double baseWeight,
77                           final ObservableSatellite satellite) {
78          super(receiver, true, date, range, sigma, baseWeight, satellite);
79  
80          addParameterDriver(emitter.getClockOffsetDriver());
81          addParameterDriver(emitter.getEastOffsetDriver());
82          addParameterDriver(emitter.getNorthOffsetDriver());
83          addParameterDriver(emitter.getZenithOffsetDriver());
84          addParameterDriver(emitter.getPrimeMeridianOffsetDriver());
85          addParameterDriver(emitter.getPrimeMeridianDriftDriver());
86          addParameterDriver(emitter.getPolarOffsetXDriver());
87          addParameterDriver(emitter.getPolarDriftXDriver());
88          addParameterDriver(emitter.getPolarOffsetYDriver());
89          addParameterDriver(emitter.getPolarDriftYDriver());
90  
91          this.emitter = emitter;
92  
93      }
94  
95      /** Get the emitter ground station.
96       * @return emitter ground station
97       */
98      public GroundStation getEmitterStation() {
99          return emitter;
100     }
101 
102     /** Get the receiver ground station.
103      * @return receiver ground station
104      */
105     public GroundStation getReceiverStation() {
106         return getStation();
107     }
108 
109     /**
110      * {@inheritDoc}
111      */
112     @Override
113     protected EstimatedMeasurementBase<BistaticRange> theoreticalEvaluationWithoutDerivatives(final int iteration,
114                                                                                               final int evaluation,
115                                                                                               final SpacecraftState[] states) {
116 
117         final GroundReceiverCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states[0]);
118         final TimeStampedPVCoordinates transitPV = common.getTransitPV();
119         final AbsoluteDate transitDate = transitPV.getDate();
120 
121         // Approximate emitter location at transit time
122         final Transform emitterToInertial =
123                 getEmitterStation().getOffsetToInertial(common.getState().getFrame(), transitDate, true);
124         final TimeStampedPVCoordinates emitterApprox =
125                 emitterToInertial.transformPVCoordinates(new TimeStampedPVCoordinates(transitDate,
126                                                                                       Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO));
127 
128         // Uplink time of flight from emitter station to transit state
129         final double tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitDate,
130                                                common.getState().getFrame());
131 
132         // Secondary station PV in inertial frame at rebound date on secondary station
133         final TimeStampedPVCoordinates emitterPV = emitterApprox.shiftedBy(-tauU);
134 
135         // Prepare the evaluation
136         final EstimatedMeasurementBase<BistaticRange> estimated =
137                         new EstimatedMeasurementBase<>(this,
138                                                        iteration, evaluation,
139                                                        new SpacecraftState[] {
140                                                            common.getTransitState()
141                                                        },
142                                                        new TimeStampedPVCoordinates[] {
143                                                            common.getStationDownlink(),
144                                                            transitPV,
145                                                            emitterPV
146                                                        });
147 
148         // Range value
149         final double tau = common.getTauD() + tauU;
150         final double range = tau * Constants.SPEED_OF_LIGHT;
151 
152         estimated.setEstimatedValue(range);
153 
154         return estimated;
155     }
156 
157     /**
158      * {@inheritDoc}
159      */
160     @Override
161     protected EstimatedMeasurement<BistaticRange> theoreticalEvaluation(final int iteration,
162                                                                         final int evaluation,
163                                                                         final SpacecraftState[] states) {
164         final SpacecraftState state = states[0];
165 
166         // Bistatic range derivatives are computed with respect to spacecraft state in inertial frame
167         // and station parameters
168         // ----------------------
169         //
170         // Parameters:
171         //  - 0..2 - Position of the spacecraft in inertial frame
172         //  - 3..5 - Velocity of the spacecraft in inertial frame
173         //  - 6..n - measurements parameters (clock offset, station offsets, pole, prime meridian, sat clock offset...)
174         final GroundReceiverCommonParametersWithDerivatives common = computeCommonParametersWithDerivatives(state);
175         final int nbParams = common.getTauD().getFreeParameters();
176         final TimeStampedFieldPVCoordinates<Gradient> transitPV = common.getTransitPV();
177         final FieldAbsoluteDate<Gradient> transitDate = transitPV.getDate();
178 
179         // Approximate emitter location (at transit time)
180         final FieldVector3D<Gradient> zero = FieldVector3D.getZero(common.getTauD().getField());
181         final FieldTransform<Gradient> emitterToInertial =
182                 getEmitterStation().getOffsetToInertial(state.getFrame(), transitDate, nbParams, common.getIndices());
183         final TimeStampedFieldPVCoordinates<Gradient> emitterApprox =
184                 emitterToInertial.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(transitDate,
185                                                                                              zero, zero, zero));
186 
187         // Uplink time of flight from emiiter to transit state
188         final Gradient tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(),
189                                                  transitPV.getDate(), state.getFrame());
190 
191         // Emitter coordinates at transmit time
192         final TimeStampedFieldPVCoordinates<Gradient> emitterPV = emitterApprox.shiftedBy(tauU.negate());
193 
194         // Prepare the evaluation
195         final EstimatedMeasurement<BistaticRange> estimated = new EstimatedMeasurement<>(this,
196                 iteration, evaluation,
197                 new SpacecraftState[] {
198                     common.getTransitState()
199                 },
200                 new TimeStampedPVCoordinates[] {
201                     common.getStationDownlink().toTimeStampedPVCoordinates(),
202                     common.getTransitPV().toTimeStampedPVCoordinates(),
203                     emitterPV.toTimeStampedPVCoordinates()
204                 });
205 
206         // Range value
207         final Gradient tau = common.getTauD().add(tauU);
208         final Gradient range = tau.multiply(Constants.SPEED_OF_LIGHT);
209 
210         estimated.setEstimatedValue(range.getValue());
211 
212         // Range first order derivatives with respect to state
213         final double[] derivatives = range.getGradient();
214         estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6));
215 
216         // Set first order derivatives with respect to parameters
217         for (final ParameterDriver driver : getParametersDrivers()) {
218             for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
219                 final Integer index = common.getIndices().get(span.getData());
220                 if (index != null) {
221                     estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]);
222                 }
223             }
224         }
225 
226         return estimated;
227     }
228 
229 }