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