1   /* Copyright 2022-2026 Romain Serra
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 java.util.Arrays;
20  
21  import org.hipparchus.linear.MatrixUtils;
22  import org.hipparchus.linear.RealMatrix;
23  import org.hipparchus.util.FastMath;
24  import org.orekit.errors.OrekitException;
25  import org.orekit.errors.OrekitMessages;
26  
27  /**
28   * Data container for measurement's expected quality (used in orbit determination).
29   * @author Romain Serra
30   * @since 14.0
31   */
32  public class MeasurementQuality {
33  
34      /** Theoretical covariance matrix. */
35      private final double[][] covarianceMatrix;
36  
37      /** Theoretical standard deviations (held separate for fast access). */
38      private final double[] sigmas;
39  
40      /** Base weights. */
41      private final double[] weights;
42  
43      /** Measurement dimension. */
44      private final int measurementDimension;
45  
46      /**
47       * Constructor with sigmas as input for unidimensional measurement and unit weight.
48       * @param standardDeviation measurement standard deviation
49       */
50      public MeasurementQuality(final double standardDeviation) {
51          this(standardDeviation, 1.);
52      }
53  
54      /**
55       * Constructor with sigmas as input and unit value for weights (assuming no correlation).
56       * @param standardDeviations measurement standard deviations matrix
57       */
58      public MeasurementQuality(final double[] standardDeviations) {
59          this(standardDeviations, 1.);
60      }
61  
62      /**
63       * Constructor with sigmas as input and same value for weights (assuming no correlation).
64       * @param standardDeviations measurement standard deviations matrix
65       * @param weight measurement component's weight (same for all)
66       */
67      public MeasurementQuality(final double[] standardDeviations, final double weight) {
68          this(standardDeviations, buildWeights(weight, standardDeviations.length));
69      }
70  
71      /**
72       * Constructor with same value for weights.
73       * @param covarianceMatrix measurement covariance matrix
74       * @param weight measurement component's weight (same for all)
75       */
76      public MeasurementQuality(final double[][] covarianceMatrix, final double weight) {
77          this(covarianceMatrix, buildWeights(weight, covarianceMatrix.length));
78      }
79  
80      /**
81       * Constructor for unidimensional measurement.
82       * @param standardDeviation measurement standard deviation
83       * @param weight measurement weight
84       */
85      public MeasurementQuality(final double standardDeviation, final double weight) {
86          this.measurementDimension = 1;
87          this.weights = new double[] {weight};
88          this.sigmas = new double[] {standardDeviation};
89          this.covarianceMatrix = new double[][] {new double[] {standardDeviation * standardDeviation}};
90      }
91  
92      /**
93       * Constructor with full covariance.
94       * @param covarianceMatrix measurement covariance matrix
95       * @param weights measurement component's weights
96       */
97      public MeasurementQuality(final double[][] covarianceMatrix, final double[] weights) {
98          if (covarianceMatrix.length != weights.length) {
99              throw new OrekitException(OrekitMessages.WRONG_MEASUREMENT_COVARIANCE_DIMENSION, covarianceMatrix.length, weights.length);
100         } else if (covarianceMatrix.length != covarianceMatrix[0].length) {
101             throw new OrekitException(OrekitMessages.COVARIANCE_MUST_BE_SQUARE);
102         }
103         this.measurementDimension = covarianceMatrix.length;
104         this.weights = weights.clone();
105         this.covarianceMatrix = new double[measurementDimension][];
106         this.sigmas = new double[measurementDimension];
107         for (int i = 0; i < measurementDimension; i++) {
108             this.covarianceMatrix[i] = covarianceMatrix[i].clone();
109             this.sigmas[i] = FastMath.sqrt(covarianceMatrix[i][i]);
110         }
111     }
112 
113     /**
114      * Constructor with sigmas.
115      * @param standardDeviations measurement standard deviations
116      * @param weights measurement component's weights
117      */
118     public MeasurementQuality(final double[] standardDeviations, final double[] weights) {
119         if (standardDeviations.length != weights.length) {
120             throw new OrekitException(OrekitMessages.WRONG_MEASUREMENT_COVARIANCE_DIMENSION, standardDeviations.length,
121                     weights.length);
122         }
123         this.measurementDimension = standardDeviations.length;
124         this.weights = weights.clone();
125         this.sigmas = standardDeviations.clone();
126         this.covarianceMatrix = new double[measurementDimension][measurementDimension];
127         for (int i = 0; i < sigmas.length; i++) {
128             covarianceMatrix[i][i] = sigmas[i] * sigmas[i];
129         }
130     }
131 
132     /**
133      * Build array of weights.
134      * @param weight value to use for all weights
135      * @param dimension total dimension
136      * @return weights
137      */
138     private static double[] buildWeights(final double weight, final int dimension) {
139         final double[] weights = new double[dimension];
140         Arrays.fill(weights, weight);
141         return weights;
142     }
143 
144     /**
145      * Getter for the measurement dimension.
146      * @return dimension
147      */
148     public int getDimension() {
149         return measurementDimension;
150     }
151 
152     /**
153      * Getter for the measurement covariance matrix.
154      * @return covariance
155      */
156     public RealMatrix getCovarianceMatrix() {
157         final double[][] coefficients = new double[measurementDimension][measurementDimension];
158         for (int i = 0; i < measurementDimension; i++) {
159             coefficients[i] = covarianceMatrix[i].clone();
160         }
161         return MatrixUtils.createRealMatrix(coefficients);
162     }
163 
164     /**
165      * Getter for the standard deviations a.k.a. sigmas for each component of the measurement.
166      * @return standard deviations
167      */
168     public double[] getStandardDeviations() {
169         return sigmas.clone();
170     }
171 
172     /** Get the correlation coefficients matrix.
173      * <p>This is the square, symmetric matrix M such that:
174      * <p>Mij = Pij/(σi.σj)
175      * <p>Where:
176      * <ul>
177      * <li>P is the covariance matrix
178      * <li>σi is the i-th standard deviation (σi² = Pii)
179      * </ul>
180      * @return the correlation coefficient matrix
181      */
182     public RealMatrix getCorrelationMatrix() {
183         // Initialize the correlation coefficients matric to the covariance matrix
184         final double[][] corrCoefMatrix = new double[measurementDimension][measurementDimension];
185 
186         // Divide by the standard deviations
187         for (int i = 0; i < corrCoefMatrix.length; i++) {
188             for (int j = 0; j < corrCoefMatrix[0].length; j++) {
189                 corrCoefMatrix[i][j] = covarianceMatrix[i][j] / (sigmas[i] * sigmas[j]);
190             }
191         }
192         return MatrixUtils.createRealMatrix(corrCoefMatrix);
193     }
194 
195     /**
196      * Getter for the weights corresponding to each component of the measurement.
197      * @return weights
198      */
199     public double[] getWeights() {
200         return weights.clone();
201     }
202 
203 }