1   /* Copyright 2002-2024 Luc Maisonobe
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.generation;
18  
19  import java.util.Map;
20  import java.util.function.Function;
21  import java.util.function.ToDoubleFunction;
22  
23  import org.hipparchus.random.CorrelatedRandomVectorGenerator;
24  import org.orekit.estimation.measurements.EstimationModifier;
25  import org.orekit.estimation.measurements.ObservableSatellite;
26  import org.orekit.estimation.measurements.QuadraticClockModel;
27  import org.orekit.estimation.measurements.gnss.AmbiguityCache;
28  import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase;
29  import org.orekit.propagation.SpacecraftState;
30  import org.orekit.propagation.sampling.OrekitStepInterpolator;
31  import org.orekit.time.AbsoluteDate;
32  import org.orekit.utils.ParameterDriver;
33  
34  
35  /** Builder for {@link OneWayGNSSPhase} measurements.
36   * @author Luc Maisonobe
37   * @since 12.0
38   */
39  public class OneWayGNSSPhaseBuilder extends AbstractMeasurementBuilder<OneWayGNSSPhase> {
40  
41      /** Cache for ambiguities.
42       * @since 12.1
43       */
44      private final AmbiguityCache cache;
45  
46      /** Wavelength of the phase observed value [m]. */
47      private final double wavelength;
48  
49      /** Satellite which receives the signal and performs the measurement. */
50      private final ObservableSatellite local;
51  
52      /** Satellite which simply emits the signal. */
53      private final ObservableSatellite remote;
54  
55      /** Clock model of the remote satellite that provides clock offset. */
56      private final QuadraticClockModel remoteClockModel;
57  
58      /** Temporary builder for clock models.
59       * @deprecated this is a temporary field, it will be removed in Orekit 13.0
60       */
61      @Deprecated
62      private Function<AbsoluteDate, QuadraticClockModel> clockBuilder;
63  
64      /** Simple constructor.
65       * @param noiseSource noise source, may be null for generating perfect measurements
66       * @param local satellite which receives the signal and performs the measurement
67       * @param remote satellite which simply emits the signal
68       * @param remoteClockModel clock model of the remote satellite that provides clock offset
69       * @param wavelength phase observed value wavelength (m)
70       * @param sigma theoretical standard deviation
71       * @param baseWeight base weight
72       * @deprecated as of 12.1, replaced by {@link #OneWayGNSSPhaseBuilder(CorrelatedRandomVectorGenerator,
73       * ObservableSatellite, ObservableSatellite, QuadraticClockModel,
74       * double, double, double, AmbiguityCache)}
75       */
76      @Deprecated
77      public OneWayGNSSPhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource,
78                                    final ObservableSatellite local, final ObservableSatellite remote,
79                                    final ToDoubleFunction<AbsoluteDate> remoteClockModel,
80                                    final double wavelength, final double sigma, final double baseWeight) {
81          this(noiseSource, local, remote, null, wavelength, sigma, baseWeight,
82               AmbiguityCache.DEFAULT_CACHE);
83          this.clockBuilder = date -> {
84              final double cM = remoteClockModel.applyAsDouble(date.shiftedBy(-1));
85              final double c0 = remoteClockModel.applyAsDouble(date);
86              final double cP = remoteClockModel.applyAsDouble(date.shiftedBy(1));
87              return new QuadraticClockModel(date, c0, 0.5 * (cP - cM), 0.5 * (cP + cM) - c0);
88          };
89      }
90  
91      /** Simple constructor.
92       * @param noiseSource noise source, may be null for generating perfect measurements
93       * @param local satellite which receives the signal and performs the measurement
94       * @param remote satellite which simply emits the signal
95       * @param remoteClockModel clock model of the remote satellite that provides clock offset
96       * @param wavelength phase observed value wavelength (m)
97       * @param sigma theoretical standard deviation
98       * @param baseWeight base weight
99       * @param cache from which ambiguity drive should come
100      * @since 12.1
101      */
102     public OneWayGNSSPhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource,
103                                   final ObservableSatellite local, final ObservableSatellite remote,
104                                   final QuadraticClockModel remoteClockModel,
105                                   final double wavelength, final double sigma, final double baseWeight,
106                                   final AmbiguityCache cache) {
107         super(noiseSource, sigma, baseWeight, local, remote);
108         this.wavelength       = wavelength;
109         this.local            = local;
110         this.remote           = remote;
111         this.remoteClockModel = remoteClockModel;
112         this.cache            = cache;
113     }
114 
115     /** {@inheritDoc} */
116     @Override
117     public OneWayGNSSPhase build(final AbsoluteDate date,
118                                  final Map<ObservableSatellite, OrekitStepInterpolator> interpolators) {
119 
120         final double sigma               = getTheoreticalStandardDeviation()[0];
121         final double baseWeight          = getBaseWeight()[0];
122         final SpacecraftState[] relevant = new SpacecraftState[] {
123             interpolators.get(local).getInterpolatedState(date),
124             interpolators.get(remote).getInterpolatedState(date)
125         };
126 
127         // temporary hack to build QuadraticClockModel from ToDoubleFunction<AbsoluteDate>
128         // for compatibility purposes
129         final QuadraticClockModel clockModel = remoteClockModel != null ?
130                                                remoteClockModel :
131                                                clockBuilder.apply(date);
132 
133         // create a dummy measurement
134         final OneWayGNSSPhase dummy = new OneWayGNSSPhase(interpolators.get(remote),
135                                                           remote.getName(),
136                                                           clockModel, date,
137                                                           Double.NaN, wavelength,
138                                                           sigma, baseWeight, local,
139                                                           cache);
140         for (final EstimationModifier<OneWayGNSSPhase> modifier : getModifiers()) {
141             dummy.addModifier(modifier);
142         }
143 
144         // set a reference date for parameters missing one
145         for (final ParameterDriver driver : dummy.getParametersDrivers()) {
146             if (driver.getReferenceDate() == null) {
147                 final AbsoluteDate start = getStart();
148                 final AbsoluteDate end   = getEnd();
149                 driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end);
150             }
151         }
152 
153         // estimate the perfect value of the measurement
154         double phase = dummy.estimateWithoutDerivatives(relevant).getEstimatedValue()[0];
155 
156         // add the noise
157         final double[] noise = getNoise();
158         if (noise != null) {
159             phase += noise[0];
160         }
161 
162         // generate measurement
163         final OneWayGNSSPhase measurement = new OneWayGNSSPhase(interpolators.get(remote),
164                                                                 remote.getName(),
165                                                                 clockModel, date,
166                                                                 phase, wavelength, sigma, baseWeight, local,
167                                                                 cache);
168         for (final EstimationModifier<OneWayGNSSPhase> modifier : getModifiers()) {
169             measurement.addModifier(modifier);
170         }
171         return measurement;
172 
173     }
174 
175 }