1   /* Copyright 2002-2025 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 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.ParameterDriver;
31  
32  import java.util.Arrays;
33  
34  public class BistaticRangeMeasurementCreator extends MeasurementCreator {
35  
36      private final Context context;
37      private final GroundStation emitter;
38      private final GroundStation receiver;
39      private final ObservableSatellite satellite;
40  
41  
42      public BistaticRangeMeasurementCreator(final Context context) {
43          this.context   = context;
44          this.emitter   = context.BRRstations.getKey();
45          this.receiver  = context.BRRstations.getValue();
46          this.satellite = new ObservableSatellite(0);
47      }
48  
49      public ObservableSatellite getSatellite() {
50          return satellite;
51      }
52  
53      public void init(SpacecraftState s0, AbsoluteDate t, double step) {
54          for (final GroundStation station : Arrays.asList(context.BRRstations.getKey(),
55                  context.BRRstations.getValue())) {
56              for (ParameterDriver driver : Arrays.asList(station.getClockOffsetDriver(),
57                      station.getEastOffsetDriver(),
58                      station.getNorthOffsetDriver(),
59                      station.getZenithOffsetDriver(),
60                      station.getPrimeMeridianOffsetDriver(),
61                      station.getPrimeMeridianDriftDriver(),
62                      station.getPolarOffsetXDriver(),
63                      station.getPolarDriftXDriver(),
64                      station.getPolarOffsetYDriver(),
65                      station.getPolarDriftYDriver())) {
66                  if (driver.getReferenceDate() == null) {
67                      driver.setReferenceDate(s0.getDate());
68                  }
69              }
70          }
71      }
72  
73      public void handleStep(final SpacecraftState currentState) {
74          final AbsoluteDate  date     = currentState.getDate();
75          final Frame         inertial = currentState.getFrame();
76          final Vector3D      position = currentState.getPosition();
77  
78          // Create a BRR measurement only if elevation for both stations is higher than 30°
79          if ((emitter.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation()  > FastMath.toRadians(30.0)) &&
80              (receiver.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation() > FastMath.toRadians(30.0))) {
81              final double clockOffset = receiver.getClockOffsetDriver().getValue();
82              final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5);
83  
84              final double downLinkDelay  = solver.solve(1000, new UnivariateFunction() {
85                  public double value(final double x) {
86                      final Transform t = receiver.getOffsetToInertial(inertial, date.shiftedBy(clockOffset + x), false);
87                      final double d = Vector3D.distance(position, t.transformPosition(Vector3D.ZERO));
88                      return d - x * Constants.SPEED_OF_LIGHT;
89                  }
90              }, -1.0, 1.0);
91              final AbsoluteDate receptionDate  = currentState.getDate().shiftedBy(downLinkDelay);
92              final Vector3D stationAtReception =
93                      receiver.getOffsetToInertial(inertial, receptionDate.shiftedBy(clockOffset), false).transformPosition(Vector3D.ZERO);
94              final double downLinkDistance = Vector3D.distance(position, stationAtReception);
95  
96              final double upLinkDelay = solver.solve(1000, new UnivariateFunction() {
97                  public double value(final double x) {
98                      final Transform t = emitter.getOffsetToInertial(inertial, date.shiftedBy(clockOffset - x), false);
99                      final double d = Vector3D.distance(position, t.transformPosition(Vector3D.ZERO));
100                     return d - x * Constants.SPEED_OF_LIGHT;
101                 }
102             }, -1.0, 1.0);
103             final AbsoluteDate emissionDate   = currentState.getDate().shiftedBy(-upLinkDelay);
104             final Vector3D stationAtEmission  =
105                    emitter.getOffsetToInertial(inertial, emissionDate.shiftedBy(clockOffset), false).transformPosition(Vector3D.ZERO);
106             final double upLinkDistance = Vector3D.distance(position, stationAtEmission);
107             addMeasurement(new BistaticRange(emitter, receiver, receptionDate.shiftedBy(clockOffset),
108                     downLinkDistance + upLinkDistance, 1.0, 10, satellite));
109         }
110 
111     }
112 
113 }