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 org.hipparchus.analysis.UnivariateFunction;
20  import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
21  import org.hipparchus.analysis.solvers.UnivariateSolver;
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.hipparchus.util.FastMath;
24  import org.orekit.estimation.Context;
25  import org.orekit.frames.Frame;
26  import org.orekit.frames.Transform;
27  import org.orekit.propagation.SpacecraftState;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.utils.Constants;
30  import org.orekit.utils.PVCoordinates;
31  import org.orekit.utils.ParameterDriver;
32  
33  import java.util.Arrays;
34  
35  /**
36   * Creates a list of {@link BistaticRangeRate bistatic range-rate measurements}.
37   * @author Pascal Parraud
38   */
39  public class BistaticRangeRateMeasurementCreator extends MeasurementCreator {
40  
41      private final Context context;
42      private final GroundStation emitter;
43      private final GroundStation receiver;
44      private final ObservableSatellite satellite;
45  
46      public BistaticRangeRateMeasurementCreator(final Context context) {
47          this.context   = context;
48          this.emitter   = context.BRRstations.getKey();
49          this.receiver  = context.BRRstations.getValue();
50          this.satellite = new ObservableSatellite(0);
51      }
52  
53      public ObservableSatellite getSatellite() {
54          return satellite;
55      }
56  
57      public void init(SpacecraftState s0, AbsoluteDate t, double step) {
58          for (final GroundStation station : Arrays.asList(context.BRRstations.getKey(),
59                                                           context.BRRstations.getValue())) {
60              for (ParameterDriver driver : Arrays.asList(station.getClockOffsetDriver(),
61                                                          station.getEastOffsetDriver(),
62                                                          station.getNorthOffsetDriver(),
63                                                          station.getZenithOffsetDriver(),
64                                                          station.getPrimeMeridianOffsetDriver(),
65                                                          station.getPrimeMeridianDriftDriver(),
66                                                          station.getPolarOffsetXDriver(),
67                                                          station.getPolarDriftXDriver(),
68                                                          station.getPolarOffsetYDriver(),
69                                                          station.getPolarDriftYDriver())) {
70                  if (driver.getReferenceDate() == null) {
71                      driver.setReferenceDate(s0.getDate());
72                  }
73              }
74          }
75      }
76  
77      public void handleStep(final SpacecraftState currentState) {
78  
79          final AbsoluteDate  date     = currentState.getDate();
80          final Frame         inertial = currentState.getFrame();
81          final Vector3D      position = currentState.getPosition();
82          final Vector3D      velocity = currentState.getPVCoordinates().getVelocity();
83  
84          // Create a BRR measurement only if elevation for both stations is higher than 30°
85          if ((emitter.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation()  > FastMath.toRadians(30.0)) &&
86              (receiver.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation() > FastMath.toRadians(30.0))) {
87  
88              // The solver used
89              final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5);
90  
91              final double downLinkDelay  = solver.solve(1000, new UnivariateFunction() {
92                  public double value(final double x) {
93                      final Transform t = receiver.getOffsetToInertial(inertial, date.shiftedBy(x), false);
94                      final double d = Vector3D.distance(position, t.transformPosition(Vector3D.ZERO));
95                      return d - x * Constants.SPEED_OF_LIGHT;
96                  }
97              }, -1.0, 1.0);
98  
99              final AbsoluteDate receptionDate = currentState.getDate().shiftedBy(downLinkDelay);
100             final PVCoordinates receiverPV   = receiver.getOffsetToInertial(inertial, receptionDate, false)
101                                                        .transformPVCoordinates(PVCoordinates.ZERO);
102 
103             // line of sight at reception
104             final Vector3D receptionLOS = (position.subtract(receiverPV.getPosition())).normalize();
105 
106             // relative velocity, spacecraft-station, at the date of reception
107             final Vector3D deltaVr = velocity.subtract(receiverPV.getVelocity());
108 
109             final double upLinkDelay = solver.solve(1000, new UnivariateFunction() {
110                 public double value(final double x) {
111                     final Transform t = emitter.getOffsetToInertial(inertial, date.shiftedBy(-x), false);
112                     final double d = Vector3D.distance(position, t.transformPosition(Vector3D.ZERO));
113                     return d - x * Constants.SPEED_OF_LIGHT;
114                 }
115             }, -1.0, 1.0);
116 
117             final AbsoluteDate emissionDate = currentState.getDate().shiftedBy(-upLinkDelay);
118             final PVCoordinates emitterPV   = emitter.getOffsetToInertial(inertial, emissionDate, false)
119                                                      .transformPVCoordinates(PVCoordinates.ZERO);
120 
121             // line of sight at emission
122             final Vector3D emissionLOS = (position.subtract(emitterPV.getPosition())).normalize();
123 
124             // relative velocity, spacecraft-station, at the date of emission
125             final Vector3D deltaVe = velocity.subtract(emitterPV.getVelocity());
126 
127             // range rate at the date of reception
128             final double rr = deltaVr.dotProduct(receptionLOS) + deltaVe.dotProduct(emissionLOS);
129 
130             addMeasurement(new BistaticRangeRate(emitter, receiver, receptionDate, rr, 1.0, 10, satellite));
131 
132         }
133 
134     }
135 }