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.generation;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.hipparchus.random.CorrelatedRandomVectorGenerator;
25  import org.orekit.estimation.measurements.EstimatedMeasurementBase;
26  import org.orekit.estimation.measurements.EstimationModifier;
27  import org.orekit.estimation.measurements.ObservableSatellite;
28  import org.orekit.estimation.measurements.ObservedMeasurement;
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  /** Base class for {@link MeasurementBuilder measurements builders}.
35   * @param <T> the type of the measurement
36   * @author Luc Maisonobe
37   * @since 9.3
38   */
39  public abstract class AbstractMeasurementBuilder<T extends ObservedMeasurement<T>> implements MeasurementBuilder<T> {
40  
41      /** Noise source (may be null). */
42      private final CorrelatedRandomVectorGenerator noiseSource;
43  
44      /** Modifiers that apply to the measurement.*/
45      private final List<EstimationModifier<T>> modifiers;
46  
47      /** Theoretical standard deviation. */
48      private final double[] sigma;
49  
50      /** Base weight. */
51      private final double[] baseWeight;
52  
53      /** Satellites related to this measurement. */
54      private final ObservableSatellite[] satellites;
55  
56      /** Start of the measurements time span. */
57      private AbsoluteDate spanStart;
58  
59      /** End of the measurements time span. */
60      private AbsoluteDate spanEnd;
61  
62      /** Simple constructor.
63       * @param noiseSource noise source, may be null for generating perfect measurements
64       * @param sigma theoretical standard deviation
65       * @param baseWeight base weight
66       * @param satellites satellites related to this builder
67       */
68      protected AbstractMeasurementBuilder(final CorrelatedRandomVectorGenerator noiseSource,
69                                           final double sigma, final double baseWeight,
70                                           final ObservableSatellite... satellites) {
71          this(noiseSource,
72               new double[] {
73                   sigma
74               }, new double[] {
75                   baseWeight
76               }, satellites);
77      }
78  
79      /** Simple constructor.
80       * @param noiseSource noise source, may be null for generating perfect measurements
81       * @param sigma theoretical standard deviation
82       * @param baseWeight base weight
83       * @param satellites satellites related to this builder
84       */
85      protected AbstractMeasurementBuilder(final CorrelatedRandomVectorGenerator noiseSource,
86                                           final double[] sigma, final double[] baseWeight,
87                                           final ObservableSatellite... satellites) {
88          this.noiseSource = noiseSource;
89          this.modifiers   = new ArrayList<>();
90          this.sigma       = sigma.clone();
91          this.baseWeight  = baseWeight.clone();
92          this.satellites  = satellites.clone();
93      }
94  
95      /** {@inheritDoc}
96       * <p>
97       * This implementation stores the time span of the measurements generation.
98       * </p>
99       */
100     @Override
101     public void init(final AbsoluteDate start, final AbsoluteDate end) {
102         spanStart = start;
103         spanEnd   = end;
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     public void addModifier(final EstimationModifier<T> modifier) {
109         modifiers.add(modifier);
110     }
111 
112     /** {@inheritDoc} */
113     @Override
114     public List<EstimationModifier<T>> getModifiers() {
115         return Collections.unmodifiableList(modifiers);
116     }
117 
118     /** Get the start of the measurements time span.
119      * @return start of the measurements time span
120      */
121     protected AbsoluteDate getStart() {
122         return spanStart;
123     }
124 
125     /** Get the end of the measurements time span.
126      * @return end of the measurements time span
127      */
128     protected AbsoluteDate getEnd() {
129         return spanEnd;
130     }
131 
132     /** Generate a noise vector.
133      * @return noise vector (null if we generate perfect measurements)
134      */
135     protected double[] getNoise() {
136         return noiseSource == null ? null : noiseSource.nextVector();
137     }
138 
139     /** Get the theoretical standard deviation.
140      * <p>
141      * The theoretical standard deviation is a theoretical value
142      * used for normalizing the residuals. It acts as a weighting
143      * factor to mix appropriately measurements with different units
144      * and different accuracy. The value has the same dimension as
145      * the measurement itself (i.e. when a residual is divided by
146      * this value, it becomes dimensionless).
147      * </p>
148      * @return expected standard deviation
149      * @see #getBaseWeight()
150      */
151     protected double[] getTheoreticalStandardDeviation() {
152         return sigma.clone();
153     }
154 
155     /** Get the base weight associated with the measurement
156      * <p>
157      * The base weight is used on residuals already normalized thanks to
158      * {@link #getTheoreticalStandardDeviation()} to increase or
159      * decrease relative effect of some measurements with respect to
160      * other measurements. It is a dimensionless value, typically between
161      * 0 and 1 (but it can really have any non-negative value).
162      * </p>
163      * @return base weight
164      * @see #getTheoreticalStandardDeviation()
165      */
166     protected double[] getBaseWeight() {
167         return baseWeight.clone();
168     }
169 
170     /** {@inheritDoc} */
171     @Override
172     public ObservableSatellite[] getSatellites() {
173         return satellites.clone();
174     }
175 
176     /**
177      * Build a dummy observed measurement.
178      *
179      * @param date          measurement date
180      * @param interpolators interpolators relevant for this builder
181      * @return dummy observed measurement
182      * @since 13.0
183      */
184     protected abstract T buildObserved(AbsoluteDate date,
185                                        Map<ObservableSatellite, OrekitStepInterpolator> interpolators);
186 
187     /** {@inheritDoc} */
188     @Override
189     public EstimatedMeasurementBase<T> build(final AbsoluteDate date,
190                                              final Map<ObservableSatellite, OrekitStepInterpolator> interpolators) {
191 
192         final SpacecraftState[] relevant = new SpacecraftState[satellites.length];
193         for (int i = 0; i < relevant.length; ++i) {
194             relevant[i] = interpolators.get(satellites[i]).getInterpolatedState(date);
195         }
196 
197         // create a dummy observed measurement
198         final T observed = buildObserved(date, interpolators);
199         for (final EstimationModifier<T> modifier : getModifiers()) {
200             observed.addModifier(modifier);
201         }
202 
203         // set a reference date for parameters missing one
204         for (final ParameterDriver driver : observed.getParametersDrivers()) {
205             if (driver.getReferenceDate() == null) {
206                 final AbsoluteDate start = getStart();
207                 final AbsoluteDate end   = getEnd();
208                 driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end);
209             }
210         }
211 
212         // estimate the perfect value of the measurement
213         final EstimatedMeasurementBase<T> estimated = observed.estimateWithoutDerivatives(relevant);
214         final double[] value = estimated.getEstimatedValue();
215 
216         // add the noise
217         final double[] noise = getNoise();
218         if (noise != null) {
219             for (int i = 0; i < value.length; ++i) {
220                 value[i] += noise[i];
221             }
222         }
223 
224         // update the dummy measurement (which is referenced by the estimated measurement)
225         observed.setObservedValue(value);
226 
227         return estimated;
228 
229     }
230 
231 }